IOS: UIImageView set border white radius and shadow - ios

Here is my code for set the border, shadow and corner
// set border
[self.avatarImageView.layer setBorderColor: [[UIColor whiteColor] CGColor]];
[self.avatarImageView.layer setBorderWidth: 2.0];
// set shadow
[self.avatarImageView.layer setShadowOffset:CGSizeZero];
[self.avatarImageView.layer setShadowOpacity:1.0];
self.avatarImageView.clipsToBounds = NO;
// set corner
self.avatarImageView.layer.cornerRadius = 10.0;
self.avatarImageView.layer.masksToBounds = YES;
If I only use the code for set border and set corner, it work well like this
But if I add the code set corner, I will got the result like this (the border and corner radius work, but the shadow is gone)
However the code for set corner work perfect if it stay alone.
Please guide me what to do. Any help would be appreciate
Update
Follow #ozgur answer. Add 2 lines to my code, it will give a very beautiful view but the shadow is a little bit smaller
self.avatarImageView.layer.shouldRasterize = YES;
self.avatarImageView.layer.shadowPath = [UIBezierPath bezierPathWithRoundedRect:self.avatarImageView.bounds cornerRadius:10].CGPath;

Rounded corners requires masksToBounds to be set to YES. Because of that nothing past the boundaries (like a shadow) can be shown, because it will be masked/clipped off. If you disable masksToBounds so that it will show then the rounded corners wont work because they are unable to mask/clip your image into the rounded shape because you disabled masksToBounds.
Therefor, you can't do both at the same time on one view—so you need TWO views.
You need to make a UIView the same dimensions as your UIImageView and make the UIImageView a subview of your UIView.
Then on your UIImageView set masksToBounds to YES, and on its' superview (the UIView with the same dimensions) set masksToBounds to NO and add the properties accordingly.
Change your code to this: (typed it all w/o using xCode so it's possible I have typos)
UIView *avatarImageViewHolder = [[UIView alloc] initWithFrame:self.avatarImageView.frame];
avatarImageViewHolder.backgroundColor = [UIColor clearColor];
[avatarImageView.superview addSubview:avatarImageViewHolder];
avatarImageViewHolder.center = avatarImageView.center;
[avatarImageViewHolder addSubview:avatarImageView];
avatarImageView.center = CGPointMake(avatarImageViewHolder.frame.size.width/2.0f, avatarImageViewHolder.frame.size.height/2.0f);
self.avatarImageView.layer.masksToBounds = YES;
avatarImageViewHolder.layer.masksToBounds = NO;
// set avatar image corner
self.avatarImageView.layer.cornerRadius = 10.0;
// set avatar image border
[self.avatarImageView.layer setBorderColor: [[UIColor whiteColor] CGColor]];
[self.avatarImageView.layer setBorderWidth: 2.0];
// set holder shadow
[avatarImageViewHolder.layer setShadowOffset:CGSizeZero];
[avatarImageViewHolder.layer setShadowOpacity:1.0];
avatarImageViewHolder.clipsToBounds = NO;

You need to add a container view and move your imageview inside that container view, Use this code after doing so:
CALayer *imageViewLayer= self.imageView.layer;
imageViewLayer.cornerRadius= 20.0f;
imageViewLayer.masksToBounds= YES;
CALayer *containerLayer = self.containerView.layer;
containerLayer.borderColor= [UIColor whiteColor].CGColor;
containerLayer.borderWidth= 3.0f;
containerLayer.cornerRadius= 20.0f;
containerLayer.shadowOffset = CGSizeMake(0, 0);
containerLayer.shadowColor = [UIColor blackColor].CGColor;
containerLayer.shadowRadius = 10.0f;
containerLayer.shadowOpacity = 0.80f;
containerLayer.masksToBounds= NO;
containerLayer.shadowPath = [[UIBezierPath bezierPathWithRect:containerLayer.bounds] CGPath];
Feel free to tweak the settings as per your need. Enjoy!

You should update layer.shadowPath if you don't want the shadow to get clipped:
self.avatarImageView.layer.shouldRasterize = YES;
self.avatarImageView.layer.shadowPath = [UIBezierPath bezierPathWithRoundedRect:self.avatarImageView.bounds cornerRadius:10].CGPath;
For this trick to work, clipsToBounds and his brother masksToBounds should be set to NO.
Thus, if you have an image whose size is greater than the avatarView's own bounds, you should define avatarImageView as plain UIView, create another imageView to display the image set its masksToBounds to YES and add it as a subview to avatarImageView so that you'll have a good cornered and shadowed view capable of displaying the clipped image.

You should set self.avatarImageView.clipsToBounds = YES; in set corner

Related

UILabel Background Color Leaks to Border

I'm creating a UILabel to which I set the background color and corner radius with the following code:
self.scoreLabel.backgroundColor = [UIColor DISRed];// custom red`
self.scoreLabel.layer.masksToBounds = YES;
self.scoreLabel.layer.cornerRadius = self.scoreLabel.frame.size.width/2;
self.scoreLabel.layer.borderWidth = 8.0;
self.scoreLabel.layer.borderColor = [[UIColor DISNavy] CGColor];
However the background's color seems to be leaking to the edge of the border (see image). Any ideas why? Any idea on how to fix it?
I was also facing the same problem. It was a silly mistake. I always forget to tick clipToBounds in case of cornerRadius.
So, just ticking the Clip to Bounds for UILabel in Storyboard fixed my problem.
And yes, we need to keep the below code too:
label.layer.masksToBounds = true
I ran into the same problem with the UIButton's background color leaking around the edge of its border.
Instead of setting the UIButton background color on the UIButton, set it on the UIButton's layer.
Replace:
self.scoreLabel.backgroundColor = [UIColor DISRed];// custom red`
With this:
self.scoreLabel.layer.backgroundColor = [[UIColor DISRed] CGColor];// custom red`
I created my own UILabel and background colour does not seem to be leaking.
Write this in .h file of your project.
UILabel *label;
Write this in .m file of your project.
label=[[UILabel alloc]initWithFrame:CGRectMake(100, 300, 100, 100)];//Set frame of label in your viewcontroller.
[label setBackgroundColor:[UIColor redColor]];//Set background color of label.
[label setText:#"Label"];//Set text in label.
[label setTextColor:[UIColor blackColor]];//Set text color in label.
[label setTextAlignment:NSTextAlignmentCenter];//Set text alignment in label.
[label.layer setCornerRadius:50.0];//Set corner radius of label to change the shape.
[label.layer setBorderWidth:8.0f];//Set border width of label.
[label setClipsToBounds:YES];//Set its to YES for Corner radius to work.
[label.layer setBorderColor:[UIColor greenColor].CGColor];//Set Border color.
[self.view addSubview:label];//Add it to the view of your choice.
It is probably anti aliasing issue. you can better fix it by adding a bezier path around the corners.
CAShapeLayer *subLayer = [[CAShapeLayer alloc] init];
[subLayer setFillColor:[UIColor clearColor].CGColor];
[subLayer setStrokeColor:[UIColor whiteColor].CGColor];
[subLayer setLineWidth:1.0];
[subLayer setPath:[UIBezierPath bezierPathWithRoundedRect:imageView.bounds cornerRadius:imageView.layer.cornerRadius].CGPath];
[imageView.layer addSublayer:subLayer];
For those who are still facing the issue of border color leaking out:
Go through the below code, please note you will need to set frames & border width as per your requirement, I'm setting the position as view's center
let badgeSize: CGFloat = 10
let redBadge = UIView(frame: CGRect(x: view.center.x, y:view.center.y, width: badgeSize, height: badgeSize))
redBadge.layer.borderColor = UIColor.white.cgColor
redBadge.layer.borderWidth = 2
redBadge.backgroundColor = .red
redBadge.layer.cornerRadius = badgeSize * 0.5
redBadge.clipsToBounds = true
redBadge.layer.masksToBounds = true
redBadge.maskLayerOnView(radius: badgeSize * 0.5)
view.addSubview(redBadge)
Secondly, we need to write an extension on UIView
extension UIView{
func maskLayerOnView(radius: CGFloat){
let maskLayer = CAShapeLayer()
maskLayer.path = UIBezierPath(roundedRect: self.bounds,
byRoundingCorners: [.allCorners],
cornerRadii: CGSize(width: radius,
height: radius)).cgPath
self.layer.mask = maskLayer
}
}
This code snippet removes the border color separating out, one can replicate this behaviour on any kind of views.
For detailed explanation please see this article.
Well, a lot of answers...
I found the problem persists, as long as the UIViews background color is used and not the background color of the UIViews 'layer'. In addition, of course, masking needs to be enabled.
For a UICollectionViewCell subclass the code is:
- (void)prepare
{
self.contentView.layer.backgroundColor = UIColor.redColor.CGColor;
self.contentView.layer.cornerRadius = 25.0;
self.contentView.layer.borderColor = UIColor.blackColor.CGColor;
self.contentView.layer.borderWidth = 1.0;
//Enable to optimize image views: self.contentView.layer.shouldRasterize = YES;
//Enable to optimize image views: self.contentView.layer.rasterizationScale = UIScreen.mainScreen.scale;
self.contentView.layer.masksToBounds = YES;
self.contentView.clipsToBounds = YES;
}
To make the setting of the background color more comfortable and less error prone, some could add this:
/*
setBackgroundColor:
*/
- (void)setBackgroundColor:(UIColor *)p_BackgroundColor
{
super.backgroundColor = nil;
self.contentView.layer.backgroundColor = p_BackgroundColor.CGColor;
self.contentView.layer.masksToBounds = YES;
self.contentView.clipsToBounds = YES;
}

Rounded UIImageView with border - raw edges

I'm trying to get smooth corners, but without success.
This is how it looks:
I have Subclass of UIImageView where in layoutSubviews I have the logic for putting the rounded corner for uiimageview, and it's clipped to bounds.
But I'm not getting why some images are drawn with these raw edges, and they are OUT of the border layer...
Because it's clipped, I can't make a custom layer with different frame for border, because whole superlayer is clipped.
- (void)layoutSubviews
{
[super layoutSubviews];
[self layoutIfNeeded];
[self.layer setCornerRadius:self.frame.size.width/2];
[self.layer setBorderWidth:BORDERSIZE];
[self.layer setBorderColor:[BORDERCOLOR CGColor]];
[self setClipsToBounds:YES];
}
To make the circular imageview use this following code in viewDidLoad. For clipped to bounds set it to yes.
self.MyImageView.layer.cornerRadius = self.MyImageView.frame.size.width / 2;
self.MyImageView.clipsToBounds = YES;
To apply the border use this code in viewDidLoad:
self.MyImageView.layer.borderWidth = 3.0f;
self.MyImageView.layer.borderColor = [UIColor whiteColor].CGColor;
This will definitely works out.

Removing a custom sublayer from a custom uitableviewcell

I need rounded, variable-height tableview cells.
I use the following code to create the rounded background:
- (void)roundView:(UIView *)view withRadius:(float)radius andColour:(UIColor *)colour
{
view.backgroundColor = [UIColor whiteColor];
CAShapeLayer *layer = [[CAShapeLayer alloc] init];
CGMutablePathRef pathRef = CGPathCreateMutable();
CGRect bounds = CGRectInset(view.bounds, 0.0f, 0.0f);
CGPathAddRoundedRect(pathRef, nil, bounds, radius, radius);
layer.path = pathRef;
CFRelease(pathRef);
layer.fillColor = colour.CGColor;
[view.layer insertSublayer:layer atIndex:0];
}
The problem is that if a tall cell is rounded, when it is reused, the earlier sublayer is still there and although the new (lesser) height is correct, the appearance is that the bottom edge of the box is not rounded. Presumably, the (larger) pre-existing layer is being clipped.
An obvious thought was to remove the sublayer, but I can't a way of doing it. I've tried creating a new cell, without reusing one, but this doesn't seem possible.
Create a custom layer property in your cell class, assign your layer to it and call -removeFromSuperLayer on it in cell's -prepareForReuse.
I recalled somewhere that I'd done this before to a button...
self.buttonCancel.layer.cornerRadius = 10;
self.buttonCancel.clipsToBounds = YES;
and it works here too...
cell.textBlurb.layer.cornerRadius = 10;
cell.textBlurb.clipsToBounds = YES;

iOS6 Add Shadow to A Container UIView With A Corner Radius

How can an encompassing UIView have both a shadow and a corner radius at the same time?
I have tried other solutions suggested on SO multiple times, but unfortunately they do not seem to work for iOS6 (or at least not for me)
So I thought I might post this, so that an iOS6 solution can be found.
I have a container UIView which contains two subviews
- a custom UIImageView
- a custom UIView
I would like the entire UIView to have a corner radius of 2.5, but I would also like the UIView to have a shadow. However, so far, I get only 1 of these 2 desires, never both at the same time.
Here is my code, I have different versions of it with my different attempts with SO solutions, but this is just one of my versions.
self.layer.shouldRasterize = YES;
self.layer.rasterizationScale = [UIScreen mainScreen].scale;
self.layer.cornerRadius = 2.5;
self.layer.masksToBounds = YES;
self.layer.shadowColor = [UIColor colorWithWhite:0.0 alpha:0.1].CGColor; //0.1
self.layer.shadowPath = [UIBezierPath bezierPathWithRect:self.bounds].CGPath;
self.layer.shadowOpacity = 1.0;
self.layer.shadowRadius = 3.0;
^here self is the containing custom UIView with two subviews described above
Does anyone know of an iOS6 solution to this problem?
UPDATE
So, I do not need a border colour, so I was not adding that when I saw the solutions, but this time I added that, using the solution in the comment below, and it seems that the UIView is getting rounded, but I really want the combined UIImageView and UIView to get rounded.
So basically, the UIImageView is on top, and the UIView is on the bottom.
So how do I get only the top of the UIImageView to be rounded, and only the bottom of the UIView to be rounded.
Thanks.
Note: shadows work as one whole object, but the corner radius is not working as one whole object?
I figured it out.
self.layer.shouldRasterize = YES;
self.layer.rasterizationScale = [UIScreen mainScreen].scale;
self.layer.shadowColor = [UIColor colorWithWhite:0.0 alpha:0.8].CGColor;
self.layer.shadowPath = [UIBezierPath bezierPathWithRoundedRect:self.layer.bounds cornerRadius:self.layer.cornerRadius].CGPath;
self.layer.shadowOpacity = 1.0;
self.layer.shadowRadius = 3.0;
UIView *container = [[UIView alloc] initWithFrame:CGRectMake(0, 0, frame.size.width, frame.size.height)];
[self addSubview:container];
[container addSubview:self.someCustomUIView];
[container addSubview:self.someCustomImageView];
container.layer.cornerRadius = 2.5;
container.layer.masksToBounds = YES;
So basically:
I set the shadow of the main UIView.
I created a container subview which contains two other subviews
I set the corner radius of the container subview
Voila! It works!
I hope this works for other people who have multiple subviews in one UIView
I'd like to thank everyone for their help. :)
I think you should change this line of code:
self.layer.masksToBounds = YES;
to this one
self.layer.masksToBounds = NO;
If you set masksToBounds to YES, then you won't see anything that extends beyond the view's bounds, and this is the case with a shadow.
This code is from my current project (iOS 6) and it works fine. I can see both rounded corners and a shadow.
self.layer.masksToBounds = NO;
self.layer.cornerRadius = 5.0;
self.layer.shadowColor = [UIColor blackColor].CGColor;
self.layer.shadowOffset = CGSizeMake(0, -1);
self.layer.shadowOpacity = 0.6;
UIBezierPath *shadowPath = [UIBezierPath bezierPathWithRect: self.layer.bounds];
self.layer.shadowPath = shadowPath.CGPath;

iOS slide up an image over an image, revealing the underneath one. (A-la jQuery.slide())

I'm trying to do the following thing -
I have two version of the same image, one colored and one black and white. I want the B&W to "Slide up" revealing the one under it.
I tried setting the height and the frame, but couldn't get it to work like I want it to.
For example , this would be a combination of both UIImages where one is revealing the other, while not moving:
Any ideas? :)
OK, here is a different method using a mask on the grey view. I actually tested this with a blueView and a greyView, where the grey covers up the blue. Put a mask layer on the grey layer so that the grey layer is visible. Now animate the mask away from the grey layer, making the grey layer disappear without moving it, the blue shows thru where the grey disappears.
If you want to experiment with this, create an empty project in XCode with a single view, and put this code inside viewDidLoad. That's how I tested this.
self.view.backgroundColor = [UIColor whiteColor];
UIView* blueView = [[[UIView alloc] init] autorelease];
blueView.backgroundColor = [UIColor blueColor];
blueView.frame = CGRectMake(50,50,100,100);
UIView* grayView = [[[UIView alloc] init] autorelease];
grayView.backgroundColor = [UIColor grayColor];
grayView.frame = CGRectMake(50,50,100,100);
[self.view addSubview:blueView];
[self.view addSubview:grayView]; // gray covers the blue
// Create a mask to allow the grey view to be visible and cover up the blue view
CALayer* mask = [CALayer layer];
mask.contentsScale = grayView.layer.contentsScale; // handle retina scaling
mask.frame = grayView.layer.bounds;
mask.backgroundColor = [UIColor blackColor].CGColor;
grayView.layer.mask = mask;
// Animate the position of the mask off the grey view, as the grey becomes unmasked,
// that part of the grey "dissappears" and is no longer covering the blue underneath
CABasicAnimation* a = [CABasicAnimation animationWithKeyPath:#"position"];
a.duration = 4;
a.fromValue = [NSValue valueWithCGPoint:mask.position];
CGPoint newPosition = mask.position;
newPosition.y += mask.bounds.size.height;
a.toValue = [NSValue valueWithCGPoint:newPosition];
a.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
[mask addAnimation:a forKey:#"colorize"]; // animate presentation
mask.position = newPosition; // update actual position
Don't forget this line at the top with the #imports:
#import <QuartzCore/QuartzCore.h>
One way is to stack the UIView's so that the B&W image on top of the color image, where the color image is completely covered (hidden) by the B&W image.
[self.view insertSubview:bwImage aboveSubview:colorImage];
Only the B&W view is visible. Now set clipsToBounds on the B&W view:
bwImage.clipsToBounds = YES;
Finally animate the height in the bounds of the bwImage so that its height shrinks down to 0, clipping the B&W image and revealing the color image behind it. Something like this:
[UIView animateWithDuration:1.0 animations:^{
CGRect b = bwImage.bounds;
b.size.height = 0;
bwImage.bounds = b;
}];
I haven't tried this but hopefully you get the idea.

Resources