CALayer shadow with Spread & Size attributes? - ios

i'm struggling creating a drop shadow around a CALayer. For the effect i'm trying to achieve i need to have access to the "intensity" of the shadow. Somehow like the "spread" slider in Photoshop's Layer Styles. I think the "ShadowRadius" property of CALayer is equivalent to Photoshop's "Size" slider.
Any suggestions? Is maybe a radial gradient an option?

Set the layer's shadowOffset,shadowOpacity, and shadowRadius and you should have what you are looking for. Try this for a shadow project directly "underneath" the layer with a blur of 5 pixels, which with the right color, could also make it look like a glow:
CALayer *layer = myView.layer;
layer.shadowOpacity = 0.50;
layer.shadowRadius = 5.0;
layer.shadowOffset = (CGSize){.width=0.0,.height=0.0};

You can control "spread" by setting custom shadowPath property on a CALayer.
CGFloat radius = CGRectGetWidth(view.bounds)/2.0;
// this is a sample code for a circle, but you can use any shape you want
CGPathRef shadowPathRef = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, 2.0 * radius, 2.0 * radius) cornerRadius:radius].CGPath;
layer.shadowPath = shadowPathRef;
If you set radius to a bigger value, shadow spread will be bigger.

Related

"Behind the scenes" difference between cornerRadius and bezierPathWithRoundedRect

Premise: I am rounding the edges of a view with a solid background (to make something like an oval). There are many ways to do this, but the two most common I've see recommended are:
1) set the cornerRadius property on the view layer and bind the mask layer to the view layer bounds (through clipsToBounds or masksToBounds) (https://developer.apple.com/reference/quartzcore/calayer/1410818-cornerradius)
ex:
view.layer.cornerRadius = 2
view.layer.maskToBounds = true
2) create a Bezier Path with [bezierPathWithRoundedRect:byRoundingCorners:cornerRadii] and use it to create a mask layer which you set to the view's layer's mask. (https://developer.apple.com/reference/uikit/uibezierpath/1624368-bezierpathwithroundedrect)
ex:
UIBezierPath* bezierPath = [UIBezierPath bezierPathWithRoundedRect: view.bounds
byRoundingCorners:UIRectCornerAllCorners
cornerRadii: CGSizeMake(2, 2)];
CAShapeLayer* maskLayer = [CAShapeLayer layer];
maskLayer.frame = view.bounds;
maskLayer.path = bezierPath.CGPath;
view.layer.mask = maskLayer;
Question: Both ways work well for me and I see no significant performance of one over the other (admittedly, I'm not using it heavily). In which situations should I use a Bezier path over setting the corner radius (assuming the corner radii are equal)? Do these two methods to creating rounded edges do the same thing behind the scenes? Are there certain platforms that won't support both solutions?
Best Regards

How can I add a drop shadow effect beneath image views on iOS?

I want to add a drop shadow effect underneath my UIImageView instances, like in the screenshot below:
How can I achieve this, either through storyboards or code (Objective-C)?
//Drop Shadow
[view.layer setShadowColor: [UIColor grayColor].CGColor];
[view.layer setShadowOpacity:0.8];
[view.layer setShadowRadius:3.0];
[view.layer setShadowOffset:CGSizeMake(2.0, 2.0)];
Please Try this once :-
UIBezierPath *shadowPath = [UIBezierPath bezierPathWithRect:view.bounds];
view.layer.masksToBounds = NO;
view.layer.shadowColor = [UIColor blackColor].CGColor;
view.layer.shadowOffset = CGSizeMake(0.0f, 5.0f);
view.layer.shadowOpacity = 0.5f;
view.layer.shadowPath = shadowPath.CGPath;
Explanation :-
As seen in the last step there are a few different properties needed when adding a shadow using Core Animation. If your are not familiar with Core Animation don’t worry, just try to focus on the shadow code and not the CALayer on the view.
shadowOffset is a CGSize representing how far to offset the shadow from the path.
shadowColor is the color of the shadow. Shadow colors should always be opaque, because the opacity will be set by the shadowOpacity property. The shadowColor property is a CGColor not a UIColor.
shadowRadius is the width of the shadow along the shadow path
shadowOpacity determines the opacity of the shadow.
shadowPath is probably the most important of the properties. While a shadow can be drawn without specifying a path, for performance reasons you should always specify one. This path tells Core Animation what the opaque regions of the view are, and without it, things slow down severely! It is a CGPath, which is most easily created using UIBezierPath (iOS only) as shown in step 2.
See more in http://nscookbook.com/2013/01/ios-programming-recipe-10-adding-a-shadow-to-uiview/
Those are not lights, but shadow. You can use the layer to draw shadow for UI elements.

Attempting to mask a circle around an image not working

I have an image that I am attempting to mask a circle around so the image appears round. This somewhat works but the circle comes to a point on the top and bottom.
profileImageView.layer.cornerRadius = profileImageView.frame.size.width/2;
profileImageView.layer.masksToBounds = YES;
Should this code be drawing a perfect circle? It seems to draw a circle in one place but in two other places, its not working correctly.
I have had the best results masking the image view with a CAShapeLayer:
CGFloat radius = self.profileImageView.frame.size.width / 2.0;
UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(radius, radius) radius:radius startAngle:0 endAngle:M_PI * 2.0 clockwise:TRUE];
CAShapeLayer *layer = [CAShapeLayer layer];
layer.path = path.CGPath;
layer.lineWidth = 0;
self.profileImageView.layer.mask = layer;
Should this code be drawing a perfect circle?
Not necessarily. After all, the width and the height of this layer might not be the same. And even if they are, dividing by 2 might not give you a radius that fits perfectly into an integral number of points as they are mapped to pixels on the screen.
It really would be better, if what you want is a mask that's a circle, to give this layer an actual mask that is an actual circle. Misusing the corner radius as you are doing is just lazy (and, as you've discovered, it's error-prone).

How to create a CGPath which exactly match a CALayer bounds when the layer rotate or scale?

We have a CALayer which shows image with edit information. Such as rotate, scale and translate. I want to make a CGPath which exactly match the layer bounds. When the layer rotate, my path should also rotate and its four corners should match the CALayer's four corners. The CGPath is actually used as a gray mask to show the clipped area of the image.
I try following code, but it does not work.
f = self.imageLayer.frame;
t = self.imageLayer.affineTransform;
CGPathAddRect(path, &t, f);
The CALayer has its own CGAffineTransform. All edit information are applied via the CGAffineTransform.
Any hint will be appreciated, Thanks a lot.
If I got your question right you could be using UIBezierPath's usesEvenOddFillRule to cut around your image layer bounds dimming the rest of the visible screen. Basically you create a very large rectangular path (for some reasons CGRectInfinite doesn't work here) and cut out the area around your image layer. The trick is to use kCAFillRuleEvenOdd which flips what is considered inside and outside.
Something like should work:
let cutPath = UIBezierPath(roundedRect: imageLayer.bounds, cornerRadius: 0)
let clipPath = UIBezierPath(rect: CGRectMake(-10e6, -10e6, 10e7, 10e7)) // CGRectInfinite isn't working here ?
clipPath.appendPath(cutPath)
clipPath.usesEvenOddFillRule = true
let shape = CAShapeLayer()
shape.contentsScale = UIScreen.mainScreen().scale
shape.lineWidth = 2
shape.fillColor = UIColor(red:0.1, green:0.1, blue:0.1, alpha:0.5).CGColor
shape.fillRule = kCAFillRuleEvenOdd
shape.strokeColor = UIColor.whiteColor().CGColor
shape.path = clipPath.CGPath
imageLayer.addSublayer(shape)
// do a transformations here
imageLayer.transform = CATransform3DMakeRotation(10.0, 1.0, 1.0, 0.8)
Which results

core gore graphics and alpha values

I am draw some circle on a map with
CGContextFillEllipseInRect
This works great, but the only issue is that I have the alpha value set to .5
If tow or more circle overlap the the overlap area is darker because of the two alpha values being added I suppose. Is there an easy way to maintain the same alpha value regardless if the circle overlap?
Thanks
If you're working with closed shapes and drawing them all in the same drawrect (or at least in the same context) you can accomplish what you want by using UIBezierPath.
// create a path with a circle
UIBezierPath* path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(50, 50) radius:50.0f startAngle:0 endAngle:M_PI * 2.0f clockwise:YES];
// add another circle to the path
[path appendPath:[UIBezierPath bezierPathWithArcCenter:CGPointMake(100, 100) radius:50.0f startAngle:0 endAngle:M_PI * 2.0f clockwise:YES]];
// draw them both, since they are in a single path they will be treated as a single shape
[path fill];
Draw the circles at full opacity, and set the opacity of the view/layer to 0.5.
EDIT:
To clarify, I assume you know how to stop drawing your circles at half opacity, since you managed to set it up that way in the first place. So make the opacity of your circles be 1.0. Then, set your view's opacity to 0.5 by doing the following:
view.alpha = 0.5;
I'm assuming you have a UIView subclass, so this can either be in the place in your code where you create the view or it could be inside the subclass in your init method for it. So if you're doing it from within the UIView subclass, it would be self instead of view:
self.alpha = 0.5;
Also, this will make all of the drawing you do in this view be 0.5 opacity. If you want to have some content that's full opacity and some that's half opacity, the simplest solution I can give you is to have two different views, one for the half opacity content, the other for the full opacity content. There are better ways to accomplish this, but for the sake of communicating easily a solution, this is probably easiest.

Resources