Removing a custom sublayer from a custom uitableviewcell - ios

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;

Related

IOS: UIImageView set border white radius and shadow

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

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;
}

How to handle UIView rounded corners (using CAShapeLayer) on rotation

I have a UITableViewCell containing a UIView. I would like to round all the corners of the UIView when the cell is the only row in the section (i.e. not selected in my implementation). If the cell is selected then I would only like to round the top two corners of the UIView. I am attempting to do this in the custom tableviewcell's layoutSubviews.
- (void)layoutSubviews
{
[super layoutSubviews];
self.roundedView.layer.mask = nil;
if (self.sectionSelected) {
self.maskLayer = [CAShapeLayer layer];
self.maskLayer.path = [UIBezierPath bezierPathWithRoundedRect:self.roundedView.bounds byRoundingCorners:(UIRectCornerTopLeft|UIRectCornerTopRight) cornerRadii:CGSizeMake(10, 10)].CGPath;
self.roundedView.layer.mask = self.maskLayer;
} else {
self.maskLayer = [CAShapeLayer layer];
self.maskLayer.path = [UIBezierPath bezierPathWithRoundedRect:self.roundedView.bounds byRoundingCorners:UIRectCornerAllCorners cornerRadii:CGSizeMake(10, 10)].CGPath;
self.roundedView.layer.mask = self.maskLayer;
}
}
Unfortunately this method does not work all the time. If I have a the cell selected in portrait (self.sectionSelected = YES). Then deselect (self.sectionSelected = NO). Then rotate to landscape and select the cell again. It displays the cell the size it is suppose to be in portrait. So I believe it is still applying the portrait layer mask and not the landscape one. If I scroll it out of view and then back it in, it updates to the appropriate width. Also of note, I call [self.tableView reloadData]; in didRotateFromInterfaceOrientation.
Any ideas for avoiding this issue? Thanks in advance
Resolved by simply adding "self.contentView.autoresizingMask = UIViewAutoresizingFlexibleWidth;"

How to round off one corner of a resizable UIView in IOS?

I'm using this code to round off one corner of my UIView:
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:
self.view.bounds byRoundingCorners:(UIRectCornerTopLeft) cornerRadii:
CGSizeMake(10.0, 10.0)];
CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
maskLayer.frame = self.view.bounds;
maskLayer.path = maskPath.CGPath;
self.view.layer.mask = maskLayer;
self.view.layer.masksToBounds = NO;
This code works, as long as I don't ever resize the view. If I make the view larger, the new area does not appear because it's outside the bounds of the mask layer (this mask layer does not automatically resize itself with the view). I could just make the mask as large as it will ever need to be, but it could be full-screen on the iPad so I'm worried about performance with a mask that big (I'll have more than one of these in my UI). Also, a super-sized mask wouldn't work for the situation where I need the upper right corner (alone) to be rounded off.
Is there a simpler, easier way to achieve this?
Update: here is what I'm trying to achieve: http://i.imgur.com/W2AfRBd.png (the rounded corner I want is circled here in green).
I have achieved a working version of this, using a subclass of UINavigationController and overriding viewDidLayoutSubviews like so:
- (void)viewDidLayoutSubviews {
CGRect rect = self.view.bounds;
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:rect
byRoundingCorners:UIRectCornerTopLeft cornerRadii:CGSizeMake(8.0, 8.0)];
self.maskLayer = [CAShapeLayer layer];
self.maskLayer.frame = rect;
self.maskLayer.path = maskPath.CGPath;
self.view.layer.mask = self.maskLayer;
}
I then instantiate my UINavigationController subclass with my view controller, and then I offset the frame of the nav controller's view by 20px (y) to expose the status bar and leave a 44-px high navigation bar, as shown in the picture.
The code is working, except that it doesn't handle rotation very well at all. When the app rotates, viewDidLayoutSubviews gets called before the rotation and my code creates a mask that fits the view after rotation; this creates an undesirable blockiness to the rotation, where bits that should be hidden are exposed during the rotation. Also, whereas the app's rotation is perfectly smooth without this mask, with the mask being created the rotation becomes noticeably jerky and slow.
The iPad app Evomail also has rounded corners like this, and their app suffers from the same problem.
The problem is, CoreAnimation properties do not animate in UIKit animation blocks. You need to create a separate animation which will have the same curve and duration as the UIKit animation.
I created the mask layer in viewDidLoad. When the view is about to be layout, I only modify the path property of the mask layer.
You do not know the rotation duration inside the layout callback methods, but you do know it right before rotation (and before layout is triggered), so you can keep it there.
The following code works well.
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
//Keep duration for next layout.
_duration = duration;
}
-(void)viewWillLayoutSubviews
{
[super viewWillLayoutSubviews];
UIBezierPath* maskPath = [UIBezierPath bezierPathWithRoundedRect:self.view.bounds byRoundingCorners:UIRectCornerTopLeft cornerRadii:CGSizeMake(10, 10)];
CABasicAnimation* animation;
if(_duration > 0)
{
animation = [CABasicAnimation animationWithKeyPath:#"path"];
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
[animation setDuration:_duration];
//Set old value
[animation setFromValue:(id)((CAShapeLayer*)self.view.layer.mask).path];
//Set new value
[animation setToValue:(id)maskPath.CGPath];
}
((CAShapeLayer*)self.view.layer.mask).path = maskPath.CGPath;
if(_duration > 0)
{
[self.view.layer.mask addAnimation:animation forKey:#"path"];
}
//Zero duration for next layout.
_duration = 0;
}
I know this is a pretty hacky way of doing it but couldn't you just add a png over the top of the corner?
Ugly I know, but it won't affect performance, rotation will be fine if its a subview and users won't notice.
Two ideas:
Resize the mask when the view is resized. You don't get automatic resizing of sublayers the way you get automatic resizing of subviews, but you still get an event, so you can do manual resizing of sublayers.
Or... If this a view whose drawing and display you are in charge of, make the rounding of the corner a part of how you draw the view in the first place (by clipping). That is in fact the most efficient approach.
You could subclass the view you are using and override "layoutSubviews"method. This one gets called everytime your view dimensions change.
Even if "self.view"(referenced in your code) is your viewcontroller's view, you can still set this view to a custom class in your storyboard. Here's the modified code for the subclass:
- (void)layoutSubviews {
[super layoutSubviews];
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:
self.bounds byRoundingCorners:(UIRectCornerTopLeft) cornerRadii:
CGSizeMake(10.0, 10.0)];
CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
maskLayer.frame = self.bounds;
maskLayer.path = maskPath.CGPath;
self.layer.mask = maskLayer;
self.layer.masksToBounds = NO;
}
I think you should create a custom view that updates itself any time it is needed, which means anytime that setNeedsDisplay is called.
What I'm suggesting is to create a custom UIView subclass to be implemented as follows:
// OneRoundedCornerUIView.h
#import <UIKit/UIKit.h>
#interface OneRoundedCornerUIView : UIView //Subclass of UIView
#end
// OneRoundedCornerUIView.m
#import "OneRoundedCornerUIView.h"
#implementation OneRoundedCornerUIView
- (void) setFrame:(CGRect)frame
{
[super setFrame:frame];
[self setNeedsDisplay];
}
// Override drawRect as follows.
- (void)drawRect:(CGRect)rect
{
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:
self.bounds byRoundingCorners:(UIRectCornerTopLeft) cornerRadii:
CGSizeMake(10.0, 10.0)];
CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
maskLayer.frame = self.bounds;
maskLayer.path = maskPath.CGPath;
self.layer.mask = maskLayer;
self.layer.masksToBounds = NO;
}
#end
Once you've done this you simply need to make your view an OneRoundedCornerUIView instance instead of an UIView one and your view will be updated smoothly every time you resize or change its frame. I've just done some testing and it seems to work perfectly.
This solution can also be easily customised in order to have a view for which you can easily set which corners should be on and which corners should not from your View Controller. Implementation as follows:
// OneRoundedCornerUIView.h
#import <UIKit/UIKit.h>
#interface OneRoundedCornerUIView : UIView //Subclass of UIView
// This properties are declared in the public API so that you can setup from your ViewController (it also works if you decide to add/remove corners at any time as the setter of each of these properties will call setNeedsDisplay - as shown in the implementation file)
#property (nonatomic, getter = isTopLeftCornerOn) BOOL topLeftCornerOn;
#property (nonatomic, getter = isTopRightCornerOn) BOOL topRightCornerOn;
#property (nonatomic, getter = isBottomLeftCornerOn) BOOL bottomLeftCornerOn;
#property (nonatomic, getter = isBottomRightCornerOn) BOOL bottomRightCornerOn;
#end
// OneRoundedCornerUIView.m
#import "OneRoundedCornerUIView.h"
#implementation OneRoundedCornerUIView
- (void) setFrame:(CGRect)frame
{
[super setFrame:frame];
[self setNeedsDisplay];
}
- (void) setTopLeftCornerOn:(BOOL)topLeftCornerOn
{
_topLeftCornerOn = topLeftCornerOn;
[self setNeedsDisplay];
}
- (void) setTopRightCornerOn:(BOOL)topRightCornerOn
{
_topRightCornerOn = topRightCornerOn;
[self setNeedsDisplay];
}
-(void) setBottomLeftCornerOn:(BOOL)bottomLeftCornerOn
{
_bottomLeftCornerOn = bottomLeftCornerOn;
[self setNeedsDisplay];
}
-(void) setBottomRightCornerOn:(BOOL)bottomRightCornerOn
{
_bottomRightCornerOn = bottomRightCornerOn;
[self setNeedsDisplay];
}
// Override drawRect as follows.
- (void)drawRect:(CGRect)rect
{
UIRectCorner topLeftCorner = 0;
UIRectCorner topRightCorner = 0;
UIRectCorner bottomLeftCorner = 0;
UIRectCorner bottomRightCorner = 0;
if (self.isTopLeftCornerOn) topLeftCorner = UIRectCornerTopLeft;
if (self.isTopRightCornerOn) topRightCorner = UIRectCornerTopRight;
if (self.isBottomLeftCornerOn) bottomLeftCorner = UIRectCornerBottomLeft;
if (self.isBottomRightCornerOn) bottomRightCorner = UIRectCornerBottomRight;
UIRectCorner corners = topLeftCorner | topRightCorner | bottomLeftCorner | bottomRightCorner;
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:
self.bounds byRoundingCorners:(corners) cornerRadii:
CGSizeMake(10.0, 10.0)];
CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
maskLayer.frame = self.bounds;
maskLayer.path = maskPath.CGPath;
self.layer.mask = maskLayer;
self.layer.masksToBounds = NO;
}
#end
I'm a fan of doing what #Martin suggests. As long as there isn't animated content behind the rounded-corner then you can pull this off - even with a bitmap image displayed behind the frontmost view needing the rounded corner.
I created a sample project to mimic your screenshot. The magic happens in a UIView subclass called TSRoundedCornerView. You can place this view anywhere you want - above the view you want to show a rounded corner on, set a property to say what corner to round (adjust the radius by adjusting the size of the view), and setting a property that is the "background view" that you want to be visible in the corner.
Here's the repo for the sample: https://github.com/TomSwift/testRoundedCorner
And here's the drawing magic for the TSRoundedCornerView. Basically we create an inverted clip path with our rounded corner, then draw the background.
- (void) drawRect:(CGRect)rect
{
CGContextRef gc = UIGraphicsGetCurrentContext();
CGContextSaveGState(gc);
{
// create an inverted clip path
// (thanks rob mayoff: http://stackoverflow.com/questions/9042725/drawrect-how-do-i-do-an-inverted-clip)
UIBezierPath* bp = [UIBezierPath bezierPathWithRoundedRect: self.bounds
byRoundingCorners: self.corner // e.g. UIRectCornerTopLeft
cornerRadii: self.bounds.size];
CGContextAddPath(gc, bp.CGPath);
CGContextAddRect(gc, CGRectInfinite);
CGContextEOClip(gc);
// self.backgroundView is the view we want to show peering out behind the rounded corner
// this works well enough if there's only one layer to render and not a view hierarchy!
[self.backgroundView.layer renderInContext: gc];
//$ the iOS7 way of rendering the contents of a view. It works, but only if the UIImageView has already painted... I think.
//$ if you try this, be sure to setNeedsDisplay on this view from your view controller's viewDidAppear: method.
// CGRect r = self.backgroundView.bounds;
// r.origin = [self.backgroundView convertPoint: CGPointZero toView: self];
// [self.backgroundView drawViewHierarchyInRect: r
// afterScreenUpdates: YES];
}
CGContextRestoreGState(gc);
}
I thought about this again and I think there is a simpler solution. I updated my sample to showcase both solutions.
The new solution is to simply create a container view that has 4 rounded corners (via CALayer cornerRadius). You can size that view so only the corner you're interested in is visible on screen. This solution doesn't work well if you need 3 corners rounded, or two opposite (on the diagonal) corners rounded. I think it works in most other cases, including the one you've described in your question and screenshot.
Here's the repo for the sample: https://github.com/TomSwift/testRoundedCorner
Try this. Hope this will helps you.
UIView* parent = [[UIView alloc] initWithFrame:CGRectMake(10,10,100,100)];
parent.clipsToBounds = YES;
UIView* child = [[UIView alloc] new];
child.clipsToBounds = YES;
child.layer.cornerRadius = 3.0f;
child.backgroundColor = [UIColor redColor];
child.frame = CGRectOffset(parent.bounds, +4, -4);
[parent addSubView:child];
If you want to do it in Swift I could advice you to use an extension of an UIView. By doing so all subclasses will be able to use the following method:
import QuartzCore
extension UIView {
func roundCorner(corners: UIRectCorner, radius: CGFloat) {
let maskPath = UIBezierPath(roundedRect: self.bounds, byRoundingCorners: corners, cornerRadii: CGSizeMake(radius, radius))
var maskLayer = CAShapeLayer()
maskLayer.frame = self.bounds;
maskLayer.path = maskPath.CGPath;
self.layer.mask = maskLayer;
}
}
self.anImageView.roundCorner(UIRectCorner.TopRight, radius: 10)

UITableViewCell with CAShapeLayer bug with appearing

I have a table view cell with custom backgroundView. This background view have an overriden method:
+ (Class)layerClass
{
return [CAShapeLayer class];
}
In code I specify the path, stroke color, fill color and line width for layer of background view of my cells. Next I try to insert section into table view using method:
- (void)insertSections:(NSIndexSet *)sections
withRowAnimation:(UITableViewRowAnimation)animation;
And at this point I get a bug: layers of inserted cells are displayed only after animation of inserting. That is, during the animation cells have no background and border.
UPDATE:
This is how I set custom background for my cells:
UITableViewCell* cell = /* initializing cell */
CGRect frame = cell.frame;
frame.size.width = tableView.frame.size.width;
frame.origin = CGPointZero;
cell.backgroundView = [[MYCellBackgroundView alloc] initWithFrame:frame];
CAShapeLayer* shapeLayer = (CAShapeLayer *)cell.backgroundView.layer;
CGMutablePathRef path = CGPathCreateMutable();
/* building path with some arcs and lines */
shapeLayer.path = path;
CGPathRelease(path);
shapeLayer.fillColor = FILL_COLOR.CGColor;
shapeLayer.strokeColor = BORDER_COLOR.CGColor;
shapeLayer.lineWidth = LINE_WIDTH;
And I forgot to specify that I use CAShapeLayer only for customizing view of cells, not for animation.

Resources