Add dropshadow in coregraphics - goes outside of context bounds? - iphone

I want to add a dropshadow to a button i've drawn in coregraphics. Other than making the button smaller than the frame, (which is messy coding as in future you'll forget about that and wonder why your button isn't the size it should be), what options do i have to draw the shadow? It's a custom shape, too.

The easiest way is to add it to you CALayer:
#include <QuartzCore/QuartzCore.h>
// iOS 4.0 or later
[self.layer setShadowColor:[[UIColor blackColor] CGColor]];
[self.layer setShadowOffset:CGSizeMake(1.0, 1.0)];
[self.layer setShadowOpacity:0.5];
[self.layer setShadowRadius:1.f];
If that won't work for you, let me know and I will post some code CG code.

Assuming I got the question correctly, you're looking for a way to stop clipping of the shadow. This is easy to achieve, assuming you have a view hierarchy in which the shadowed view resides, just turn off clipsToBounds on the superview of the shadowed view (and if needed on the superview's superview).
Be careful though, as there may've been other things your views were clipping and you didn't want to be seen (although normally this shouldn't be a problem).

Related

How to add a drop shadow to an MKMapView without disabling clipsToBounds

I'm having an issue when I attempt to add a drop shadow to an MKMapView's layer, in order for the shadow to be visible I have to set the view's clipsToBounds property to false. However, doing so causes the map tiles to draw outside the view's boundaries, overlapping the shadow and cutting of parts of my view. The result looks something like this:
I'm wondering if there's a way to draw a drop shadow without disabling bounds clipping or otherwise get the drop shadow to appear without this ugly visual bug. My code for setting the drop shadow looks like this:
self.mapView.layer.borderWidth = 5.0;
self.mapView.layer.borderColor = [[UIColor whiteColor] CGColor];
self.mapView.layer.shadowOffset = CGSizeMake(0.0, 0.0);
self.mapView.layer.shadowColor = [[UIColor blackColor] CGColor];
self.mapView.layer.shadowRadius = 5.0;
self.mapView.layer.shadowOpacity = 0.2;
self.mapView.clipsToBounds = NO;
Since the layer is owned by the MKMapView, it’s generally not a great idea to be touching it yourself. (This is the kind of thing that’s likely to break in weird ways in later OS versions, and behave in unpredictable ways (it’d be interesting to see if that even works at all with the new iOS 6 3D maps). With layer-backed views on OS X, you’re not supposed to touch the layer directly at all (unless it’s layer-hosting view, but that’s a different discussion))
To get a shadow underneath, just make your own new CALayer with a shadow positioned underneath the map. Alternatively nest the MKMapView as a subview of your own UIView, and add the shadow to your view (that has no need for clipping) instead.
You have to create two views, one for the shadow and an other for the rounded corners.
More info here : UIView Round Corners with Shadow

UIImage Not Transparent

I am trying to make a view that lets a user draw with their finger. I have a png made in Pixelmator of the brush. My drawRect: method looks like this:
- (void)drawRect:(CGRect)rect
{
NSAssert(brush != nil, #"");
for (UITouch *touch in touchesNeedingDrawing)
{
//Draw the touch
[brush drawInRect:CGRectWithPoints([touch locationInView:self], [touch previousLocationInView:self]) blendMode:0 alpha:1];
}
[touchesNeedingDrawing removeAllObjects];
}
The brush image is a png with transparency, but when I run the app, there is no transparency. Does anyone know why, and how to fix it?
Edit:
I discovered that the image is transparent, but when I call drawInRect, the image draws the transparent pixels as the background color of the view. Is there a CGBlendMode I can use to fix this?
Solution is very simple, testes on my own project. In class which you updated with custom - (void)drawRect:(CGRect)rect set in -(id)init background color for self
[self setBackgroundColor:[UIColor clearColor]];
And thats all. Tested on my project.
It seems like it could be taking the current fill color of the context.
Try setting the fill color for the context with CGContextSetFillColorWithColor() to [UIColor clearColor].CGColor
If that doesn't work, the only other solution that is simple and shouldn't have a performance hit is to have 2 views:
Background View - This will be view that has the proper background color or image
Overlay View - This view will detect the touches etc and then draw all of the brush strokes on top. The background color of this view can then be [UIColor clearColor] so when you draw the brush images, the alpha will be filled with [UIColor clearColor]. Basically the same view you have now, just with a clear background.
Note: You shouldn't need to mess with the blend mode to make this work. You should be able to use the default drawInRect: method
Is the brush png loaded to an imageView? That is, the variable brush is an object of UIImageView, isn't it?
If so, perhaps simple
brush.backgroundColor = [UIColor clearColor];
will help
i think you should try destination out blend mode: kCGBlendModeDestinationOut.
You could also draw at point instead draw in rect:
[brush drawAtPoint:[touch locationInView:self] blendMode:kCGBlendModeDestinationOut alpha:1]
A possible issue is that you are setting the blendMode to 0. I suggest using the -drawInRect: method without a blend mode. The issue may also be that your view has a black background, but that is doubtful. I would also suggest attempting to display the UIImage in a UIImageView as a test. The issue may be related to the way that PixelMator exports images.
Your problem is a fundamental misconception of how drawRect: actually works. Every time you draw something into the current graphics context, everything that was there previously will be cleared (so that only the backgroundColor remains).
Since you're only drawing the current touch(es) (touchesNeedingDrawing is emptied), there's nothing under the rectangle you're filling that could show the transparency of the image you're drawing, so the background color shows through.
You basically have two options to resolve this:
1) Keep all touches that contribute to the drawing around (don't empty the touchesNeedingDrawing array) and redraw all of them every time – this is going to be easy but quite slow.
2) Draw into a buffer of some kind (e.g. a UIImage, using UIGraphicsBeginImageContext etc.). Every time your drawing changes, create a new buffer. Draw the old buffer into the new buffer and the images for the new stroke on top of it.

Create a glow around a UIView

I would like to draw a glow-ish border around a UIView which is roughly 5px from the actual UIView itself.
Please could you tell me how I could achieve this?
Probably the easiest way is to create a shadow, but use a light color instead of a dark one. Shadow details can be found here: How do I draw a shadow under a UIView? and here.
Something like this should get the ball rolling:
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
CGContextSetShadowWithColor(context, CGSizeMake(0, 0), 10,
[UIColor whiteColor].CGColor);
[super drawRect:rect];
CGContextRestoreGState(context);
}
Update: I just tried this out. You will have to use this code on the superview of the glowing view for it to work properly.
The first thing I would try is to embed the UIView within a UIView which has the glow image. If the glow effect is just an image, then you create a UIView containing the glow image that is 10 px taller and wider than the UIView being surrounded. This will allow for 5 px of extension on all 4 sides. You can do all this quickly and easily using Interface Builder.
If you want the glow effect to look really cool, considering creating a collection of glow images that when viewed as a sequence will show sort of a moving glow effect. You can then use this collection of images in the UIView and turn on animation. All UIView controls have animation support built in.
Hope this helped. Good Luck.

Border image on UIView

I want to have a UIView subclass that has a border image, but I don't want or care about this 'new' frame/bounds around the border image itself.
What I wanted to do was just use drawRect and draw outside of the rect but all drawing is clipped and I don't see a way to not clip drawing outside of this context rect.
So now I have added a sublayer to the views layer, set [self clipsToBounds] on the view and override setFrame to control my sublayers frame and always keep it at the proper size (spilling over the views frame by 40px).
The problem with this is that setFrame on a uiview by default has no animation but seTFrame on a calayer does.
I cant just disable the animations on the calayers setFrame because if I were to call setFrame on the uiview inside a uiview animation block the calayer would still have its animation disabled.
The obvious solution is to look up the current animationDuration on the uiview animation and set a matching animation on the sublayer, but I don't know if this value is available. And even if it is, I'm afraid that calling an animation from within another animation is wrong.
Unfortunately the best solution is to not use a calayer at all and just add a uiview as a subview and draw into that just like I am drawing into my layer, and hope that with autoresizingMask set to height and width that everything will 'just work'. Just seems like unnecessary overhead for such a simple task.
My solution would be to override the initWithFrame: to add the surrounding border pixels and contain the content in a subview. It probably is unneccesary overhead but definietly the "cocoa" route. It's probably going to be easier in the end too since a subview structure will allow you to edit the content seperatly from the border so you dont have to redraw the border when you redraw the content. And keeping them seperate simply makes sense from a OOP perspective.
The clipsToBounds route is probably the easiest route besides the subview structure but managing the border and content in one drawing cycle and in one object will probably be a lot more work so it'll be worth the overhead.
Excuse any typos, typed this from my iPhone.

How can I draw a shadow beyond a UIView's bounds?

I'm using the method described at How do I draw a shadow under a UIView? to draw shadow behind a view's content. The shadow is clipped to the view's bounds, although I disabled "Clip Subviews" in Interface Builder for the view. Is it possible to draw a shadow around a view and not only in a view?
I don't want to draw the shadow inside the view because the view would receive touch events for the shadow area, which really belongs to the background.
Instead of manual drawing in drawRect, consider setting properties on the the UIView's Core Animation layer for drawing a shadow.
For example:
[descriptionInput setClipsToBounds:NO];
[descriptionInput.layer setShadowColor:[[UIColor blackColor] CGColor]];
[descriptionInput.layer setShadowOpacity:0.8];
[descriptionInput.layer setShadowOffset:CGSizeMake(0.0, 3.0)];
For this to work, you need to include QuartzCore:
#import <QuartzCore/QuartzCore.h>
clipsToBounds only controls the clipping for children of a view, not drawing of that view itself; hence it's not solving your problem.
If you can draw your shadow onto a different view and add that as a child, it won't get clipped. However, I don't know how possible that is with the method you're using :(
It is not encouraged to draw outside view bounds. Maybe you can include the shadow directly in your background...
Regards,