Shadow not appearing for UIView using CALayer - iphone

I have a subclassed UIView loaded from a nib, and I cannot get a shadow to draw around it. I'm trying to get a shadow to appear around the entire view for quite some time now. I elected to place it in it's own sublayer to simplify animating it later. Here's the code:
-(void)awakeFromNib
{
self.clipsToBounds = NO;
// set up the shadow layer
CALayer *shadow = [CALayer layer];
shadow.frame = CGRectMake(self.frame.origin.x, self.frame.origin.y, self.bounds.size.width, self.bounds.size.height);
shadow.shadowColor = [UIColor blueColor].CGColor;
shadow.shadowRadius = 15.0;
shadow.opacity = 1.0;
[self.layer addSublayer:shadow];
// I set this property so I have access to it later to more easily animate it.
self.shadowLayer = shadow;
}
When I NSLog the shadowLayer property, the coordinates and frame are correct. It's matches the view it's backing.
I also set a border color and corner radius on self.layer and it appears correctly. If I put the shadow on self.layer it appears but it encompasses all the subviews of my parent UIView.
Any help is greatly appreciated.

Am assuming you have QuartzCore imported. I think you need to set & create a border to the UIView. The way to use this [self roundedLayerWithShadow:yourView.layer radius:5.0f];
- (void)roundedLayerWithShadow:(CALayer *)viewLayer
radius:(float)r
{
[viewLayer setMasksToBounds:YES];
[viewLayer setCornerRadius:r];
[viewLayer setBorderColor:[RGB(180, 180, 180) CGColor]];
[viewLayer setBorderWidth:1.0f];
[viewLayer setShadowColor:[RGB(0, 0, 0) CGColor]];
[viewLayer setShadowOffset:CGSizeMake(0, 0)];
[viewLayer setShadowOpacity:1];
[viewLayer setShadowRadius:2.0];
return;
}

I struggled with the same, and it turns out you need to set shadowOpacity to 1.0. In your code you accidentally use opacity instead of shadowOpacity. That's the same issue I had.

In general, for the shadow to appear:
the shadowOpacity should be larger than 0
the shadowRadius should be larger than 0
the masksToBounds should be set to false (to avoid the clipping of the shadow)
the shadowColor should be different than the background color of the view's superview
Swift 4.2. example implementation:
let myCustomView = MyCustomView()
myCustomView.layer.shadowColor = UIColor.black.cgColor
myCustomView.layer.shadowOpacity = 0.15
myCustomView.layer.shadowRadius = 5
myCustomView.layer.masksToBounds = false
myCustomView.layer.shadowOffset = CGSize(width: 0, height: 2)

Aside from borderRadius, your shadow layer looks very much transparent. Therefore it will not drop any shadow on shadow.superlayer.

Related

How to create an oval shape UIButton

I have to draw an oval shape button, i don't want to set the image in oval shape.
I have read that you can draw different shaper with UIBezierPath but i am unable to draw a proper oval shape.
Here is the code is am using to create a circular shape.
self.layer.borderWidth=1.0;
self.clipsToBounds = YES;
[self setTitleColor:ktextColor forState:UIControlStateNormal];
self.layer.cornerRadius = self.bounds.size.width/2;//half of the width
self.layer.borderColor=[[UIColor whiteColor]CGColor];
Any help is appreciated !!
You can set the delegate for your button's layer and implement the delegate method `displayLayer:'
-(void)displayLayer:(CALayer *)layer
{
UIGraphicsBeginImageContext(layer.frame.size);
CGContextRef context = UIGraphicsGetCurrentContext();
UIBezierPath* ovalPath = [UIBezierPath bezierPathWithOvalInRect: CGRectMake(0.0, 0.0, CGRectGetWidth(layer.frame), CGRectGetHeight(layer.frame))];
[[UIColor whiteColor] setFill];
[ovalPath fill];
[[UIColor blackColor] setStroke];
ovalPath.lineWidth = 1;
[ovalPath stroke];
UIImage *imageBuffer = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
layer.contents = (id)[imageBuffer CGImage];
}
You can also create a CAShapelayer and than add gesture to the layer as below:
-(void)generateOvalWithSize:(CGSize)size origin:(CGPoint)origin {
CAShapeLayer ovalLayer = [CAShapeLayer layer];
CGMutablePathRef ovalPath = CGPathCreateMutable();
CGPathAddEllipseInRect(ovalPath, NULL, CGRectMake(origin.x, origin.y, size.width, size.height));
CGPathCloseSubpath(ovalPath);
ovalLayer.path = ovalPath;
// Configure the apperence of the circle
ovalLayer.fillColor = [UIColor whiteColor].CGColor;
ovalLayer.strokeColor = [UIColor whiteColor].CGColor;
ovalLayer.lineWidth = 2;
// Add to parent layer
[[self layer] addSublayer:ovalLayer];
}
i have been using these two functions for a while, i haven't come across any issues yet, but if someone sees an issue please let me know.
you can pass anything that is a subclass of UIView (ie. UIButton, UICollectionViewCell, etc.)
+ (void)setCornerRadius:(CGFloat)radius forView:(UIView*)view
{
[view.layer setCornerRadius:radius];
[view.layer setMasksToBounds:YES];
}
+ (void)setBordersForView:(UIView*)view width:(CGFloat)width color:(UIColor*)color
{
view.layer.borderColor = color.CGColor;
view.layer.borderWidth = width;
}
the above yields me the effect below in a collection view:
the collection view cell has a single button which is bound all the way around the edges (auto layout) and takes up the full height and width of the cell.
then i pass in the entire cell (or self.viewForBaseLineLayout if calling from within the cell) as the view parameter in both functions listed above.
for the radius parameter i am passing 20.0f.
(but this value will vary depending on the size the view you are passing; you just have to play around with it)
i know this post is old, but maybe it will help someone new : )
You can use the OBShapedButton class Download Here

CAShapeLayer sublayer of a UIView in a UIScrollView is extremely slow

I have a custom UIView which is supposed to be a round rectangle which, when you begin editing a UILabel inside, the round rectangle will grow its shape (CABasic animation on a CAShapeLayer mask path and outline path). So to be clear, the custom UIView is organized like this:
MyCustomView has a ClipView.
ClipView uses drawRect to draw the background of the round rectangle. (clipview == view that gets clipped)
Then, I call the following code to do the CAShapeLayer manipulation:
//0. CLIP
shapeLayer = [CAShapeLayer layer];
CGRect shapeRect = self.bounds;
[shapeLayer setBounds:shapeRect];
[shapeLayer setPosition:CGPointMake((shapeRect.size.width)/2.0f, (shapeRect.size.height)/2.0f)];
[shapeLayer setFillColor:[[UIColor colorWithRed:0 green:1 blue:0 alpha:1.0f] CGColor]];
[shapeLayer setStrokeColor:[[UIColor redColor] CGColor] ];
[shapeLayer setLineWidth:1];
[shapeLayer setLineJoin:kCALineJoinRound];
[shapeLayer setOpacity:1];
CGRect shapeLayerPathRect = CGRectMake(0, 0, self.bounds.size.width, 44.0f);
shapeLayer.path = [self roundRectPathForRectangle:shapeLayerPathRect andRadius:CORNER_RADIUS];
[[self.clipView layer] setMask:shapeLayer];
//1. draw the shaded outline of a round rectangle
outlineLayer = [self topDownShadowShapeLayer]; //setup for my CAShapeLayer, involving shadow stuff, line color, etc
outlineLayer.path = [self roundRectPathForRectangle:shapeLayerPathRect andRadius:CORNER_RADIUS];
[self.clipView.layer addSublayer:outlineLayer];
Although the animation the proceeds is the quickest approach (stretching both CAShapeLayers), the entire element (MyCustomView) causes everything to be a lot slower! For example, UIView animations that go on, also when MyCustomView is inside a ScrollView it is VERY jumpy.
I'm mostly concerned about the UIScrollView problem. Why is it so choppy for a UIScrollView to translate these layers around? Is there a good solution / what's another approach that can achieve the same effect?

How can I set maskToBounds effect on CAShapeLayer(Rounded-Recr cornered UIView)?

I want to make the glow effect spread outside of thisView.
I used this code to make Half-Rounded rect cornered UIVew.
Round two corners in UIView
Here is my code.
+ (UIView *)makeUpperHalfRoundedView:(UIView *)view {
CAShapeLayer *maskLayer = [CAShapeLayer layer];
maskLayer.frame = view.bounds;
UIBezierPath *roundedPath =
[UIBezierPath bezierPathWithRoundedRect:maskLayer.bounds
byRoundingCorners:UIRectCornerTopLeft | UIRectCornerTopRight
cornerRadii:CGSizeMake(8.f, 8.f)];
maskLayer.fillColor = [[UIColor blackColor] CGColor];
maskLayer.backgroundColor = [[UIColor clearColor] CGColor];
maskLayer.path = [roundedPath CGPath];
// maskLayer.masksToBounds = NO;
//Don't add masks to layers already in the hierarchy!
UIView *superview = [view superview];
[view removeFromSuperview];
view.layer.mask = maskLayer;
[superview addSubview:view];
return view;
}
And there are two image buttons in this view closed to the boundary.
If a touch event happen on buttons, glow effect shows(showsTouchWhenHighlighted)
Before using this piece of code, I just used
thisView.layer.cornerRadius = 8;
thisVIew.layer.masksToBounds = NO;
and glow effect spread outside of 'thisView'.
But after using CAShapeLayer in 'thisView' to masking half-roused rect, the glow effect are masked by boundary.
What you're seeing is exactly how a mask is supposed to work. You are applying the mask to the layer of the superview. Let's call that view superview (just as in your code). Okay, so what will that do? It will mask superview and all its sublayers. That includes its subviews. Its subviews are actually its sublayers.
So, since the buttons are subviews of superview, they are masked like everything else inside superview.
If you don't want that to happen, don't make the buttons subviews of superview. They can sit on top of superview without being its subviews, for example.

Set border around UIImageView

I want to apply two types of border on a UIImageView:
One is the border on the layer of the UIImageView.
Second is the border around the layer of the UIImageView.
How can I do this?
Try
#define kBorderWidth 3.0
#define kCornerRadius 8.0
CALayer *borderLayer = [CALayer layer];
CGRect borderFrame = CGRectMake(0, 0, (imageView.frame.size.width), (imageView.frame.size.height));
[borderLayer setBackgroundColor:[[UIColor clearColor] CGColor]];
[borderLayer setFrame:borderFrame];
[borderLayer setCornerRadius:kCornerRadius];
[borderLayer setBorderWidth:kBorderWidth];
[borderLayer setBorderColor:[[UIColor redColor] CGColor]];
[imageView.layer addSublayer:borderLayer];
And don't forget to import QuartzCore/QuartzCore.h
This example will draw a boarder on the layer, but change it's frame slightly to make the border around the layer.
Another way
You must import
#import <QuartzCore/QuartzCore.h>
Then add code for your UIImageView
imgView.clipsToBounds = YES;
imgView.layer.cornerRadius = 8.0;
imgView.layer.borderWidth = 2.0;
imgView.layer.borderColor = [UIColor greenColor].CGColor;
An other way is to add another layer that goes a bit outside of the UIImageView's layer like so :
CALayer * externalBorder = [CALayer layer];
externalBorder.frame = CGRectMake(-1, -1, myView.frame.size.width+2, myView.frame.size.height+2);
externalBorder.borderColor = [UIColor blackColor].CGColor;
externalBorder.borderWidth = 1.0;
[myView.layer addSublayer:externalBorder];
myView.layer.masksToBounds = NO;
Swift 5
Beware when using a UIImageView; masksToBounds = false means the image will spill over
let outsideBorder = CALayer()
outsideBorder.frame = CGRect(x: -1, y: -1, width: myView.frame.size.width+2, height: myView.frame.size.height+2)
outsideBorder.borderColor = UIColor.black.cgColor
outsideBorder.borderWidth = 1.0
myView.layer.addSublayer(outsideBorder)
myView.masksToBounds = false

How to get context of a CALayer plus a few pixels around it?

I am creating a layer with a shadow effect. Instead of adding it as a sublayer to my view's layer, I would like to draw it. But I am having problems being able to draw it with the shadow effect. The problem is that the context size it based on the layer size. But how do I set the rect for the context?!
CALayer *layer = [CALayer layer];
layer.frame = ...;
layer.backgroundColor = [UIColor redColor].CGColor;
layer.cornerRadius = 2.0;
layer.masksToBounds = NO;
layer.shadowColor = [[UIColor whiteColor] CGColor];
layer.shadowOffset = CGSizeMake(0, 1);
layer.shadowRadius = 0.5;
layer.shadowOpacity = 0.2;
[self.layer addSublayer:layer];
UIGraphicsBeginImageContext(layer.bounds.size);
[layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[viewImage drawAtPoint:CGPointMake(0, 0)];
[layer removeFromSuperlayer];
With the code above, what I am getting is the box, but without a few extra pixels of padding with the shadow (on all four sides). If I increase the size of the ImageContext, all I get is more height and width, but still starting from x=0 and y=0, where I would want it to start from x=-5, y=-5 or something like that.
Thanks a bunch!
Make your context 10 pixels larger in both the x and the y.
Then before you draw your layer do a CGContextTranslateCTM(context,5,5);
Hope this helps!
I'm not sure if the following is your problem, but it just caught my eye.
[self.layer addSublayer:layer];
UIGraphicsBeginImageContext(layer.bounds.size);
[layer renderInContext:UIGraphicsGetCurrentContext()];
It looks like you have a property on this file called layer, which you are adding the shadow layer you've created to. You then get the context based on the current CALayer, as opposed to self.layer.
I typically use CALayer to modify UIImageView, and do not have a need to add sub layers, so perhaps that code is fine. Just wanted to point it out, please do let me know if which way is accurate, as I always like to learn.
~Good Luck