Create topLeft and topRight corners with border? - ios

This code creating only topLeft border, but i want topRight also. How?
UIBezierPath *maskPath;
maskPath = [UIBezierPath bezierPathWithRoundedRect:self.colorSliderBackgroundView.bounds
byRoundingCorners:(UIRectCornerTopLeft|UIRectCornerTopRight)
cornerRadii:CGSizeMake(10.0,10.0)];
CAShapeLayer *borderLayer = [[CAShapeLayer alloc] init];
borderLayer.frame = self.colorSliderBackgroundView.bounds;
borderLayer.path = maskPath.CGPath;
borderLayer.lineWidth = 1.5f;
borderLayer.strokeColor = [UIColor colorWithRed:243.0/255.0 green:243.0/255.0 blue:243.0/255.0 alpha:1.0].CGColor;
borderLayer.fillColor = [UIColor clearColor].CGColor;
[self.colorSliderBackgroundView.layer addSublayer:borderLayer];

Based on your code, it looks like you want an outlined rectangle with the top-left and top-right corners rounded, as a sublayer of another view...
Create a new view class, and set it as the custom class of your colorSliderBackgroundView.
TopCornersRoundedView.h
//
// TopCornersRoundedView.h
//
// Created by Don Mag on 10/30/19.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
IB_DESIGNABLE
#interface TopCornersRoundedView : UIView
#end
NS_ASSUME_NONNULL_END
TopCornersRoundedView.m
//
// TopCornersRoundedView.m
// ObjCXIBTest
//
// Created by Don Mag on 10/30/19.
// Copyright © 2019 Don Mag. All rights reserved.
//
#import "TopCornersRoundedView.h"
#interface TopCornersRoundedView ()
#property (strong, nonatomic) CAShapeLayer *borderLayer;
#end
#implementation TopCornersRoundedView
- (instancetype)initWithCoder:(NSCoder *)coder
{
self = [super initWithCoder:coder];
if (self) {
[self commonInit];
}
return self;
}
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self commonInit];
}
return self;
}
- (void)prepareForInterfaceBuilder {
[super prepareForInterfaceBuilder];
[self commonInit];
}
- (void) commonInit {
// instantiate the shape layer
_borderLayer = [CAShapeLayer new];
// set line width, stroke and fill colors
_borderLayer.lineWidth = 1.5f;
_borderLayer.strokeColor = [UIColor colorWithRed:243.0/255.0 green:243.0/255.0 blue:243.0/255.0 alpha:1.0].CGColor;
_borderLayer.fillColor = [UIColor clearColor].CGColor;
// add the shape layer as a sublayer of self
[self.layer addSublayer:_borderLayer];
}
- (void)layoutSubviews {
[super layoutSubviews];
// create a bezier path with top left and right corners rounded
// doing this in layoutSubviews will keep the frame size correct when
// the view changes size
UIBezierPath *maskPath;
maskPath = [UIBezierPath bezierPathWithRoundedRect:self.bounds
byRoundingCorners:(UIRectCornerTopLeft|UIRectCornerTopRight)
cornerRadii:CGSizeMake(10.0,10.0)];
_borderLayer.frame = self.bounds;
_borderLayer.path = maskPath.CGPath;
}
#end
By designating this class as IB_DESIGNABLE you will even see the result at design-time:

All you need todo is this:
UIView *view = [[UIView alloc] initWithFrame:frame];
CALayer *layer = [CALayer layer];
UIBezierPath *shadowPath = [UIBezierPath bezierPathWithRoundedRect:frame byRoundingCorners:(UIRectCornerTopLeft|UIRectCornerTopRight)
cornerRadii:CGSizeMake(3.0, 3.0)];
layer.shadowPath = shadowPath.CGPath;
view.layer.mask = layer;
Or you can check this link for further detail
how to set cornerRadius for only bottom-left,bottom-right and top-left corner textview?

Related

Corner Radius Using UIBezierPath

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

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

UITableView: offsetting selectedBackgroundView

I've created a custom cell look that creates the look of a smaller cell and the illusion of space between them:
// SETTING UP A UIView IN ORDER TO CREATE ILLUSION OF SEPARATION BETWEEN CELLS
cell.contentView.backgroundColor = [UIColor clearColor];
UIView *clearRoundedCornerView = [[UIView alloc] initWithFrame:CGRectMake(10,10,310,110)];
clearRoundedCornerView.backgroundColor = [UIColor clearColor];
clearRoundedCornerView.layer.masksToBounds = YES;
clearRoundedCornerView.layer.cornerRadius = 3.0;
clearRoundedCornerView.layer.shadowOffset = CGSizeMake(-1, 1);
clearRoundedCornerView.layer.shadowOpacity = 0.5;
// Set up Mask with 2 rounded corners
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:clearRoundedCornerView.bounds byRoundingCorners:(UIRectCornerTopLeft | UIRectCornerBottomLeft) cornerRadii:CGSizeMake(20.0, 20.0)];
CAShapeLayer *cornerMaskLayer = [CAShapeLayer layer];
[cornerMaskLayer setPath:path.CGPath];
clearRoundedCornerView.layer.mask = cornerMaskLayer;
// Make a transparent, stroked laker which will display the stroke
CAShapeLayer *strokeLayer = [CAShapeLayer layer];
strokeLayer.path = path.CGPath;
strokeLayer.fillColor = [UIColor clearColor].CGColor;
strokeLayer.strokeColor = [UIColor colorWithRed:(194/255.0) green:(77/255.0) blue:(1/255.0) alpha:1].CGColor;
strokeLayer.lineWidth = 5.0;
// Transparent view that will contain the stroke layer
UIView *strokeView = [[UIView alloc] initWithFrame:clearRoundedCornerView.bounds];
strokeView.userInteractionEnabled = NO; // in case your container view contains controls
[strokeView.layer addSublayer:strokeLayer];
[clearRoundedCornerView addSubview:strokeView];
[cell.contentView addSubview:clearRoundedCornerView];
In the following code I tried to modify selectedBackgroundView to match the view I created. The problem I am running into is that the X Y coordinates won't change and the selectedBackgroundView stays pinned to the upper left corner of the cell.
- (void)installSelectedBackgroundView{
UIView *bgCustomView = [[UIView alloc] initWithFrame:CGRectMake(10,10,310,110)];
bgCustomView.backgroundColor = [UIColor lightGrayColor];
bgCustomView.layer.masksToBounds = YES;
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:bgCustomView.bounds byRoundingCorners:(UIRectCornerTopLeft | UIRectCornerBottomLeft) cornerRadii:CGSizeMake(20.0, 20.0)];
CAShapeLayer *cornerMaskLayer = [CAShapeLayer layer];
[cornerMaskLayer setPath:path.CGPath];
bgCustomView.layer.mask = cornerMaskLayer;
self.selectedBackgroundView = bgCustomView;
[self setSelected:YES animated:YES];
}
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
[self installSelectedBackgroundView];
}
return self;
}
- (void)awakeFromNib {
[super awakeFromNib];
[self installSelectedBackgroundView];
}
Not sure why this isn't working. Changing the X Y in bgCustomView doesn't seem to accomplish anything.
Thank you.
You need to change the origin of the path in installSelectedBackgroundView. As you've noticed, the origin of the bgCustomView is ignored; that's because as the selectedBackgroundView, it will have the same origin as the cell. So, instead, you need to offset the origin of the mask layer's path,
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectOffset(bgCustomView.bounds, 10, 10) byRoundingCorners:(UIRectCornerTopLeft | UIRectCornerBottomLeft) cornerRadii:CGSizeMake(20.0, 20.0)];

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

How to render image of UIView containing UIBezierPath subviews

I have a UIView that contains some subviews - drawn using UIBezierPath. Every subview contains a scrollview having image as mask. When I try to render image of whole view (overall), instead of making image from path, it draws image of individual rect of subviews. Let me make it clear by figure:
Original View:
What I am getting after rendering Image:
I don't know what am I missing. Please guide me. Thanks.
Code:
IrregularSquare
#interface IrregularSquare : UIView
{
UIScrollView *imageResizer;
CAShapeLayer *maskLayer;
}
#end
#implementation IrregularSquare
- (id)initWithFrame:(CGRect)frame index:(NSInteger) index
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
self.backgroundColor = [UIColor clearColor];
self.layer.backgroundColor = [UIColor clearColor].CGColor;
}
return self;
}
-(void) drawRect:(CGRect)rect
{
UIBezierPath *aPath = [UIBezierPath bezierPath];
[aPath moveToPoint:CGPointMake(CGRectGetMinX(rect), CGRectGetMinY(rect))];
[aPath addLineToPoint:CGPointMake(CGRectGetMaxX(rect), CGRectGetMinY(rect))];
[aPath addLineToPoint:CGPointMake(CGRectGetMaxX(rect) - 20, CGRectGetMidY(rect) - 10)];
[aPath addLineToPoint:CGPointMake(CGRectGetMaxX(rect) - 5, CGRectGetMaxY(rect) - 10)];
[aPath addLineToPoint:CGPointMake(CGRectGetMidX(rect) + 10, CGRectGetMaxY(rect))];
[aPath addLineToPoint:CGPointMake(CGRectGetMidX(rect) - 20, CGRectGetMaxY(rect) - 20)];
[aPath addLineToPoint:CGPointMake(CGRectGetMinX(rect), CGRectGetMaxY(rect) - 20)];
[aPath closePath];
aPath.lineWidth = 2;
[[UIColor whiteColor] setStroke];
[aPath stroke];
[self initializeImageWithPath]; //this method adds scrollview to main view and assigns an image to scrollview with mask
}
-(void) initializeImageWithPath
{
CGRect aFrame = somevalue;
self.backImage = [[UIImageView alloc] initWithFrame:aFrame];
self.backImage.contentMode = UIViewContentModeScaleAspectFill;
self.backImage.clipsToBounds = YES;
imageResizer = [[UIScrollView alloc] initWithFrame:self.bounds];
imageResizer.showsHorizontalScrollIndicator = imageResizer.showsVerticalScrollIndicator = NO;
imageResizer.scrollEnabled = YES;
imageResizer.backgroundColor = [UIColor clearColor];
imageResizer.zoomScale = 1.0;
imageResizer.delegate = self;
imageResizer.clipsToBounds = YES;
[imageResizer addSubview:self.backImage];
[self addSubview: imageResizer];
maskLayer = [CAShapeLayer layer];
maskLayer.path = [aPath CGPath];
self.layer.mask = maskLayer;
}
IrregularShapesContainer.m
#implementation IrregularShapesContainer
-(void) drawSubviews
{
for(UIView *aView in self.subviews)
[aView removeFromSuperview];
IrregularSquare *sq1 = [[IrregularSquare alloc] initWithFrame:CGRectMake(HORIPADDING, VERTICALPADDING, (self.frame.size.width - (HORIPADDING * 2) - (SPACING * 2)) * .5, (self.frame.size.height - (VERTICALPADDING) - SPACING) * .5) index:1];
[self addSubview:sq1];
[sq1.layer display];
[self randomlyPlaceImage:sq1];
sq1.irrViewDelegate = self;
//many similar instances are added in following code
}
#end
I hope my Question is clear.. Any help is appreciated.

Resources