Corner Radius Using UIBezierPath - ios

I'm Trying to round my view's corners using "UIBezierPath". I Only need to round topRight and Top left.
I have used Following code
-(void)setMaskOnView:(UIView *)givenView
{
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:givenView.bounds byRoundingCorners: (UIRectCornerTopLeft|UIRectCornerTopRight) cornerRadii:CGSizeMake(10.0, 10.0)];
CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
maskLayer.frame = givenView.bounds;
maskLayer.path = maskPath.CGPath;
givenView.layer.mask = maskLayer;
}
But my TopRight Corner does not round.
I've used
UIRectCornerAllCorners
But it does not round my right corners
Any thing i'm missing??

I suggest different approach. Load image with rounded top corners and set is as contents of CALayer. Set this layer as mask of your view layer. Update size of your mask layer in layoutSubivews of a given view or viewDidLayoutSubviews of given view controller.
Loading image as layer contenst
CALayer *maskLayer = [[CALayer alloc] init];
UIImage *maskImage = [UIImage imageNamed:#"mask_image.png" inBundle:[NSBundle mainBundle] compatibleWithTraitCollection:nil];
maskLayer.contents = (__bridge id _Nullable)(maskImage.CGImage);
mainLayer.mask = maskLayer
[EDIT] Answering your questions in comments
Either using CAShapeLayer or image as mask you have to resize your mask layer so it will have the same size as masked layer. If we are talking about UITableViewCell create your own derived cell and update mask shape in layoutSubviews . Below is example code (MyTableCell is loaded from storyboard):
#interface MyTableCell ()
#property (nonatomic, strong) CAShapeLayer *maskLayer;
#end
#implementation MyTableCell
- (void)awakeFromNib
{
self.maskLayer = [[CAShapeLayer alloc] init];
self.layer.mask = self.maskLayer;
}
- (void)layoutSubviews
{
[super layoutSubviews];
self.maskLayer.path = [self maskPath].CGPath;
}
- (UIBezierPath *)maskPath
{
return [UIBezierPath bezierPathWithRoundedRect:self.bounds byRoundingCorners: (UIRectCornerTopLeft|UIRectCornerTopRight) cornerRadii:CGSizeMake(10.0, 10.0)];
}
#end

I'm using this subclass
.h
#interface UIView (custom)
- (UIView *)setRoundedCorners:(UIRectCorner)corners withRadius:(CGFloat)radius;
#end
.m
#implementation UIView (custom)
- (UIView *)setRoundedCorners:(UIRectCorner)corners withRadius:(CGFloat)radius
{
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:self.bounds
byRoundingCorners:corners
cornerRadii:CGSizeMake(radius, radius)];
CAShapeLayer *maskLayer = [CAShapeLayer layer];
maskLayer.frame = self.bounds;
maskLayer.path = maskPath.CGPath;
self.layer.mask = maskLayer;
return self;
}
#end
Using it like:
[YOURVIEW setRoundedCorners:UIRectCornerBottomLeft | UIRectCornerBottomRight | UIRectCornerTopLeft | UIRectCornerTopRight withRadius:15];

Related

How to subclass a class in ObjectiveC correctly?

I write a UITextfield with code in file called login.m
_textfield1 = [[UITextField alloc] initWithFrame:CGRectMake(50, 60, self.view.frame.size.width-50*2, 44)];
_textfield1.placeholder = #"email";
_textfield1.font = [UIFont systemFontOfSize:14.0f];
_textfield1.delegate = self;
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:_textfield1.bounds byRoundingCorners:UIRectCornerTopLeft|UIRectCornerTopRight cornerRadii:CGSizeMake(4, 4)];
CAShapeLayer *maskLayer = [CAShapeLayer layer];
maskLayer.frame = _textfield1.bounds;
maskLayer.path = maskPath.CGPath;
_textfield1.layer.mask = maskLayer;
[self.view addSubview:_textfield1];
and i subclass UITextfield with name 'NewTextField', NewTextField.h
#import <UIKit/UIKit.h>
#interface NewTextField : UITextField
#end
NewTextField.m
#import "NewTextField.h"
#implementation NewTextField
- (void)setMaskView:(UIView *)maskView {
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:maskView.bounds byRoundingCorners:UIRectCornerBottomLeft|UIRectCornerBottomRight cornerRadii:CGSizeMake(4, 4)];
CAShapeLayer *maskLayer = [CAShapeLayer layer];
maskLayer.frame = maskView.bounds;
maskLayer.path = maskPath.CGPath;
maskView.layer.mask = maskLayer;
}
#end
in file login.m, i update code
_textfield1 = [[NewTextField alloc] initWithFrame:CGRectMake(50, 60, self.view.frame.size.width-50*2, 44)];
_textfield1.placeholder = #"email";
_textfield1.font = [UIFont systemFontOfSize:14.0f];
_textfield1.delegate = self;
[_textfield1 setMaskView:_textfield1];
[self.view addSubview:_textfield1];
it works, but i don't think it is a right way. so how can I subclass a class in right way?
In NewTextField.m instead of - (void)setMaskView:(UIView *)maskView write a initializer method as bellow,
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:self.bounds byRoundingCorners:UIRectCornerBottomLeft|UIRectCornerBottomRight cornerRadii:CGSizeMake(4, 4)];
CAShapeLayer *maskLayer = [CAShapeLayer layer];
maskLayer.frame = self.bounds;
maskLayer.path = maskPath.CGPath;
self.layer.mask = maskLayer;
}
return self;
}
Technically there is no problem with subclassing UITextField like you've done, maybe I'm stating the obvious because you told us it works :D.
However if you plan just to add a mask to a UITextField, it feels unnecessary to subclass a UITextField, maybe just create helper method or a category to add a the mask to the textfield, that seems like a cleaner approach.

CAShapeLayer resulting in no image in UIImageView

I'm trying to make uiimageview top corners rounded but when CASharpeLayer is drawn image gets disappear, i don't know what causing this issue i've put below code in willDisplayCell. when cell is reused images appears properly with drawn layer
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:cell.imgProduct.bounds byRoundingCorners:UIRectCornerTopLeft | UIRectCornerTopRight cornerRadii:CGSizeMake(3.0,3.0)];
CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
maskLayer.frame = cell.imgProduct.bounds;
maskLayer.path = maskPath.CGPath;
cell.imgProduct.layer.mask = maskLayer;
cell.imgProduct.layer.masksToBounds=NO;
I've solved this by myself...:P i've drawn the mask in drawRect method of my UICollectionViewCell subclass
- (void)drawRect:(CGRect)rect {
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:self.imgProduct.bounds byRoundingCorners:UIRectCornerTopLeft | UIRectCornerTopRight cornerRadii:CGSizeMake(3.0, 3.0)];
CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
maskLayer.frame = self.imgProduct.bounds;
maskLayer.path = maskPath.CGPath;
_imgProduct.layer.mask = maskLayer;
_imgProduct.layer.masksToBounds=YES;
}

Auto layout constraint and CALayer issue

I am using this code to make my top corners to be rounded.
- (void)setMaskTo:(UIView*)view byRoundingCorners:(UIRectCorner)corners
{
UIBezierPath *rounded = [UIBezierPath bezierPathWithRoundedRect:view.bounds
byRoundingCorners:corners
cornerRadii:CGSizeMake(5.0, 5.0)];
CAShapeLayer *shape = [[CAShapeLayer alloc] init];
[shape setPath:rounded.CGPath];
view.layer.mask = shape;
}
then in initWithFrame of superview method I make this:
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectZero];
[self setMaskTo:imageView byRoundingCorners:UIRectCornerTopLeft|UIRectCornerTopRight];
But as result of it I don't see image. I am using auto layout constraint that works correct and I see image in case if I don't use rounding corner method above. But if I use it, I don't see image.
Seems if use draw rect callback of my view where image is placed, I can set rounding corners. But in this case if view contains other views it will add rounding to all subviews, if I will cycle it:
It works but for all subviews of course:
- (void)drawRect:(CGRect)rect {
// Drawing code
for (UIView *view in self.subviews)
{
[self setMaskTo:view byRoundingCorners:UIRectCornerTopLeft|UIRectCornerTopRight];
}
}
- (void)setMaskTo:(UIView*)view byRoundingCorners:(UIRectCorner)corners
{
UIBezierPath *rounded = [UIBezierPath bezierPathWithRoundedRect:view.bounds
byRoundingCorners:corners
cornerRadii:CGSizeMake(5.0, 5.0)];
CAShapeLayer *shape = [[CAShapeLayer alloc] init];
[shape setPath:rounded.CGPath];
view.layer.mask = shape;
}
So how to detect when image view has got frame, I suppose that the frame is nil at start CALayer does not understand how to interact with view.
That's happen because you instantiate the view with frame CGRectZero, then you use that bounds for the mask so nothing in the view is inside the mask and therefore nothing is shown.
As a work around you can set a tag to UIImageView instance different than 0 and search in the subviews for that
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectZero];
imageView.tag = 111; //or any other number you want
- (void)drawRect:(CGRect)rect {
// Drawing code
for (UIView *view in self.subviews)
{
if (view.tag==111){[self setMaskTo:view byRoundingCorners:UIRectCornerTopLeft|UIRectCornerTopRight];
}}
}
- (void)setMaskTo:(UIView*)view byRoundingCorners:(UIRectCorner)corners
{
UIBezierPath *rounded = [UIBezierPath bezierPathWithRoundedRect:view.bounds
byRoundingCorners:corners
cornerRadii:CGSizeMake(5.0, 5.0)];
CAShapeLayer *shape = [[CAShapeLayer alloc] init];
[shape setPath:rounded.CGPath];
view.layer.mask = shape;
}
If I understand it correctly, you want your UIView, which contains an image inside, to have rounded corners on the top side, right? Then you could just resize the mask layer in layoutSubviews.
Something like:
- (void)awakeFromNib {
[super awakeFromNib];
[self setMaskTo:self byRoundingCorners:UIRectCornerTopLeft|UIRectCornerTopRight];
}
- (void)layoutSubviews {
[super layoutSubviews];
self.layer.mask.frame = self.layer.bounds;
}
- (void)setMaskTo:(UIView*)view byRoundingCorners:(UIRectCorner)corners {
UIBezierPath *rounded = [UIBezierPath bezierPathWithRoundedRect:view.bounds
byRoundingCorners:corners
cornerRadii:CGSizeMake(5.0, 5.0)];
CAShapeLayer *shape = [[CAShapeLayer alloc] init];
[shape setPath:rounded.CGPath];
view.layer.mask = shape;
view.layer.masksToBounds = YES;
}

Apply CAShapeLayer to multiple UIImageViews inside Cells

I'm trying to round the top corners of a UIImageView inside a UICollectionViewCell. It worked with my first code but the UICollectionViews performance went way down and started lagging. In a attempt to prevent this I tried to create a variable and just generate a CAShapeLayer for the first cell and then apply the same mask to the rest of the cells. Here is my code:
In cellForItemAtIndexPath
In if(maskLayer == nil){
UIBezierPath *maskPath;
maskPath = [UIBezierPath bezierPathWithRoundedRect:cell.image.bounds byRoundingCorners:(UIRectCornerTopLeft | UIRectCornerTopRight) cornerRadii:CGSizeMake(3.0, 3.0)];
maskLayer = [[CAShapeLayer alloc] init];
maskLayer.frame = cell.image.bounds;
maskLayer.path = maskPath.CGPath;
}
cell.image.layer.mask = maskLayer;
...
Far above:
#implementation MainViewViewController{
CAShapeLayer *maskLayer;
}
But this does not work. The mast is only getting applied to the last cell that comes in to view when scrolling and gets removed from the rest. Could this solution even work and why is this not working?
Thanks in advance!
EDIT 1
Tried to creata a subclass of UIImageView called RoundedImageView as suggested by #debugger and now the code looks like this:
RoundedImageView.m:
#import "RoundedImageView.h"
#implementation RoundedImageView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
- (void)viewDidLoad{
UIBezierPath *maskPath;
maskPath = [UIBezierPath bezierPathWithRoundedRect:self.bounds byRoundingCorners:(UIRectCornerTopLeft | UIRectCornerTopRight) cornerRadii:CGSizeMake(3.0, 3.0)];
CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
maskLayer.frame = self.bounds;
maskLayer.path = maskPath.CGPath;
self.layer.mask = maskLayer;
}
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
UIBezierPath *maskPath;
maskPath = [UIBezierPath bezierPathWithRoundedRect:self.bounds byRoundingCorners:(UIRectCornerTopLeft | UIRectCornerTopRight) cornerRadii:CGSizeMake(3.0, 3.0)];
CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
maskLayer.frame = self.bounds;
maskLayer.path = maskPath.CGPath;
self.layer.mask = maskLayer;
}
In my storyboard I have set the UIImageView class to RoundedImagView and in my custom UICollectionViewCell:
#property (strong, nonatomic) IBOutlet RoundedImageView *image;
But I the viewDidLoad or drawRect in RoundedImageView.m is not getting called at all.
for that you will have to create UIImageview extension class name RoundImageview. and add the following code in the ViewDidLoad method into class:
self.contentMode = UIViewContentModeScaleAspectFill;
self.clipsToBounds = YES;
self.layer.cornerRadius = self.frame.size.width/2; // Setting the corner radius
self.layer.masksToBounds = YES;
Now in the viewcontroller class add the UIImageview extension in the header and intitialize like below:
RoundImageview *roundimageview;
and in the cellForRowAtIndexPath method use the following method to add the image:
rounfimageview.image = yourimagename
I think above method will work..just try and test it.
Create CAShapeLayer for cell. When you create a single instance and try to add for all cell, it will add to the last one.
cell.image.layer.mask = maskLayer;
Here it will not create new copy when you assign masklayer.
Edit:
How single instance of layer can be sublayer for more than one layer?.
In cellForRowAtIndexPath: do the following
if (!cell) {
// create cell
//And create mask layer
cell.image.layer.mask = maskLayer; // - And do this
}
You don't need to override drawRect:, and you don't want to put code in viewDidLoad, because that's a UIViewController method, and won't be called in a UIImageView subclass. Since the image view is made in the storyboard, you should put your code in the initWithCoder: method of the UIImageView subclass,
- (id)initWithCoder:(NSCoder *)aDecoder {
if (self = [super initWithCoder:aDecoder]) {
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:self.bounds byRoundingCorners:(UIRectCornerTopLeft | UIRectCornerTopRight) cornerRadii:CGSizeMake(3.0, 3.0)];
CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
maskLayer.frame = self.bounds;
maskLayer.path = maskPath.CGPath;
self.layer.mask = maskLayer;
}
return self;
}

rounded corner uilabel not visible (xcode 4.5.2)

I am new to IOS development, try to create top rounded corner label
by following link
How do I round only the top two corners of a UILabel?
But after applying the mask, the whole label cannot be seen, Any clue?
Here is my code.
#synthesize title;
- (void)viewDidLoad
{
[super viewDidLoad];
title.layer.borderColor=[[UIColor blueColor]CGColor];
title.layer.borderWidth=2;
UIBezierPath *maskPath;
maskPath = [UIBezierPath bezierPathWithRoundedRect:title.bounds byRoundingCorners:(UIRectCornerTopLeft | UIRectCornerTopRight) cornerRadii:CGSizeMake(10.0f, 10.0f)];
CAShapeLayer *maskLayer = [CAShapeLayer layer];
maskLayer.frame=title.bounds;
maskLayer.path = maskPath.CGPath;
title.layer.mask = maskLayer;
}
EDITED
Tried the suggestion to create the label in program, the layout become
https://docs.google.com/file/d/0B60xg2ZCEjOqeTBEWG92N0NodFU/edit?usp=sharing
The corner disappear and I still want to set the mask the "title" labal?
EDITED
Finally give up, for others benefit,I wrap the label in uiview , map the uiview to a class (i.e. Myview.m), and put the same code in method "- (void)drawRect:(CGRect)rect{}" in "Myview.m". The display showed as what I want!.
I'm not sure if this is your problem because you didn't show how the title label is created, but if you're creating it in code, you need to be sure to alloc/init the label, as well as give it a frame and then add it as a subview of its superview.
title = [[UILabel alloc] initWithFrame:CGRectMake(100.0f, 100.0f, 60.0f, 30.0f)];
title.layer.borderColor=[[UIColor blueColor]CGColor];
title.layer.borderWidth=2;
UIBezierPath *maskPath;
maskPath = [UIBezierPath bezierPathWithRoundedRect:title.bounds byRoundingCorners:(UIRectCornerTopLeft | UIRectCornerTopRight) cornerRadii:CGSizeMake(10.0f, 10.0f)];
CAShapeLayer *maskLayer = [CAShapeLayer layer];
maskLayer.frame=title.bounds;
maskLayer.path = maskPath.CGPath;
title.layer.mask = maskLayer;
[self.view addSubview:title];
first of all my advice is don't use "title" as a UILabel name because all UIViewController has a property that name is title.
After changing label name and sure IBOutlet is connected add this code:
lblTitle.layer.masksToBounds = YES;
I just figure out if you create a label on storyboard you dont need to alloc again. I wrote this code and it works;
_L_corner.layer.borderColor =[[UIColor blueColor] CGColor];
_L_corner.layer.borderWidth = 5.0f;
_L_corner.layer.cornerRadius = 3.0f;
Finally give up, for others benefit, and used another work around:-
1) wrap the label in uiview ,
2) map the uiview to a class (i.e. Myview.m),
3) put the same code in method
- (void)drawRect:(CGRect)rect{
title.layer.borderColor=[[UIColor blueColor]CGColor];
title.layer.borderWidth=2;
UIBezierPath *maskPath;
maskPath = [UIBezierPath bezierPathWithRoundedRect:title.bounds byRoundingCorners:(UIRectCornerTopLeft | UIRectCornerTopRight) cornerRadii:CGSizeMake(10.0f, 10.0f)];
CAShapeLayer *maskLayer = [CAShapeLayer layer];
maskLayer.frame=title.bounds;
maskLayer.path = maskPath.CGPath;
title.layer.mask = maskLayer;
}
in "Myview.m".
The display showed as what I want!

Resources