542

Is there a way to set cornerRadius for only top-left and top-right corner of a UIView?

I tried the following, but it end up not seeing the view anymore.

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;
Cœur
  • 37,241
  • 25
  • 195
  • 267
tom
  • 14,273
  • 19
  • 65
  • 124
  • 9
    After your edit, three things to fix: (1) the rounded path should be based on `view.bounds`, not `frame`, (2) the layer should be a `CAShapeLayer`, not `CALayer`; (3) set the layer's `path`, not `shadowPath`. – Kurt Revis Apr 16 '12 at 00:32
  • 1
    Possible duplicate of this [question](http://stackoverflow.com/q/2264083/429427) & [answer](http://stackoverflow.com/a/5826698/429427). – Stuart Sep 02 '14 at 06:49
  • Use the Bezier curve algorithm, to create curves on a CGPath. I'm pretty sure it's part of CoreGraphics. If not, http://en.wikipedia.org/wiki/Bézier_curve has some great definitions and animations. – Nate Symer Jan 01 '15 at 21:43
  • https://iosdevcenters.blogspot.com/2018/02/how-to-set-roundcorner-radius-on-view.html – Bhadresh Jun 06 '18 at 03:07
  • See my answer here: https://stackoverflow.com/a/50396485/6246128 – wcarhart Feb 22 '19 at 23:54
  • It's much easier on iOS 11 and above: https://johncodeos.com/how-to-round-only-specific-corners-to-a-uiview-in-ios-using-swift/ – Yehor Hromadskyi Aug 14 '19 at 17:14
  • iOS 11 - Interface Builder only solution: https://stackoverflow.com/a/58626264 – Bruno Bieri May 14 '20 at 13:40

31 Answers31

563

I am not sure why your solution did not work but the following code is working for me. Create a bezier mask and apply it to your view. In my code below I was rounding the bottom corners of the _backgroundView with a radius of 3 pixels. self is a custom UITableViewCell:

UIBezierPath *maskPath = [UIBezierPath
    bezierPathWithRoundedRect:self.backgroundImageView.bounds
    byRoundingCorners:(UIRectCornerBottomLeft | UIRectCornerBottomRight)
    cornerRadii:CGSizeMake(20, 20)
];

CAShapeLayer *maskLayer = [CAShapeLayer layer];

maskLayer.frame = self.bounds;
maskLayer.path = maskPath.CGPath;

self.backgroundImageView.layer.mask = maskLayer;

Swift 2.0 version with some improvements:

let path = UIBezierPath(roundedRect:viewToRound.bounds, byRoundingCorners:[.TopRight, .BottomLeft], cornerRadii: CGSizeMake(20, 20))
let maskLayer = CAShapeLayer()

maskLayer.path = path.CGPath
viewToRound.layer.mask = maskLayer

Swift 3.0 version:

let path = UIBezierPath(roundedRect:viewToRound.bounds,
                        byRoundingCorners:[.topRight, .bottomLeft],
                        cornerRadii: CGSize(width: 20, height:  20))

let maskLayer = CAShapeLayer()

maskLayer.path = path.cgPath
viewToRound.layer.mask = maskLayer

Swift extension here

lewis
  • 2,936
  • 2
  • 37
  • 72
Yunus Nedim Mehel
  • 12,089
  • 4
  • 50
  • 56
  • 2
    try in textfield. success in left, failure in right – Rainer Liao Aug 20 '15 at 09:36
  • 12
    @RainerLiao i had the same issue, i did all this on the `viewDidLoad` method, move it to `viewDidAppear` and it worked. – Lucho Sep 09 '15 at 19:31
  • how can I set this to work for any device. this only works fine for the `simulator` size int the storyboard. `ex: if the simulator size is 6s it works fine for 6s, but not of others.` how can I overcome this. – caldera.sac Sep 21 '16 at 12:19
  • 1
    Little syntax error in Swift 3 version: `path.CGPath` should be `path.cgPath` – Rob Oct 18 '16 at 12:05
  • @RainerLiao if you don't want it to flick as the view appears, change it to `viewWillAppear` instead. This also works. – Matthew Knippen Dec 13 '16 at 17:39
  • Please note that masks are quite CPU-consuming. – Andrey Gordeev Mar 02 '17 at 07:06
  • 1
    not working for bottom. i.e. bottomLeft & bottomRight – user1673099 Apr 12 '17 at 06:41
  • don't forget to add `viewToRound.layoutIfNeeded()` Thanks – Nazmul Hasan Aug 06 '17 at 08:08
  • It's a [UIView property](https://developer.apple.com/documentation/uikit/uiview/1622580-bounds) – Yunus Nedim Mehel Jul 03 '18 at 12:46
  • @YunusNedimMehel this is working well But how can I use it in my view class in IBDesignable for changing the Edge corners in story board ? – Saeed Rahmatolahi Jul 24 '18 at 04:54
  • Glad to hear. Sadly, I don't think you can do this on IB since this is an operation on the `.layer`. – Yunus Nedim Mehel Jul 24 '18 at 08:47
  • You can expose `IBDesignable` properties on `UIView` subclasses who's getters and/or setters return/change properties of the view's backing layer. I keep an extension to `UIView` around that exposes a `borderColor` property that's a `UIColor`, for example. The getter returns the border color of the backing Layer (converted from a `CGColor` to a `UIColor`) and the setter takes a `UIColor` as input and sets the layer's border `CGColor`. – Duncan C Sep 10 '22 at 21:12
434

And finally… there is CACornerMask in iOS11! With CACornerMask it can be done pretty easy:

let view = UIView()
view.clipsToBounds = true
view.layer.cornerRadius = 10
view.layer.maskedCorners = [.layerMaxXMinYCorner, .layerMinXMinYCorner] // Top right corner, Top left corner respectively
Almas Adilbek
  • 4,371
  • 10
  • 58
  • 97
Sergei Belous
  • 4,708
  • 1
  • 16
  • 20
  • 6
    this definitely works best. we always had issues with the other solutions when rounding the edges of dynamically sizing tableview cells. – cornr Nov 13 '17 at 12:05
  • what will do for older version? self.layer.maskedCorners = [.layerMaxXMaxYCorner, .layerMinXMaxYCorner,.layerMaxXMinYCorner] – SHEBIN Jan 29 '18 at 19:11
  • @SHEBIN, try solutions from the other answers on this page – Sergei Belous Jan 30 '18 at 08:11
  • 1
    The other answers don't work when rotating the device, this code works for rotation and allows you to round certain corners, this should be the accepted answer. – Cloud9999Strife Oct 03 '18 at 12:37
  • Do not needed to set "view.clipsToBounds = true". Else it can have some side-effects on Shadow(if you have applied shadows as well) – Muhammad Nayab Dec 06 '19 at 11:07
  • 4
    This should be the top answer. It is the most direct and relevant answer to the OP's question. – Richard Poutier Jan 26 '21 at 23:45
  • 2
    Let's get this in top place –  Jun 01 '22 at 11:10
  • CACornerMask extension constants so humans can understand: https://gist.github.com/derekleerock/19a767c528b53ffa381a51c49f220595 – Derek Lee Oct 10 '22 at 23:27
371

Pay attention to the fact that if you have layout constraints attached to it, you must refresh this as follows in your UIView subclass:

override func layoutSubviews() {
    super.layoutSubviews()
    roundCorners(corners: [.topLeft, .topRight], radius: 3.0)
}

If you don't do that it won't show up.


And to round corners, use the extension:

extension UIView {
   func roundCorners(corners: UIRectCorner, radius: CGFloat) {
        let path = UIBezierPath(roundedRect: bounds, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
        let mask = CAShapeLayer()
        mask.path = path.cgPath
        layer.mask = mask
    }
}


Additional view controller case: Whether you can't or wouldn't want to subclass a view, you can still round a view. Do it from its view controller by overriding the viewWillLayoutSubviews() function, as follows:

class MyVC: UIViewController {
    /// The view to round the top-left and top-right hand corners
    let theView: UIView = {
        let v = UIView(frame: CGRect(x: 10, y: 10, width: 200, height: 200))
        v.backgroundColor = .red
        return v
    }()
    
    override func loadView() {
        super.loadView()
        view.addSubview(theView)
    }
    
    override func viewWillLayoutSubviews() {
        super.viewWillLayoutSubviews()

        // Call the roundCorners() func right there.
        theView.roundCorners(corners: [.topLeft, .topRight], radius: 30)
    }
}
Stéphane de Luca
  • 12,745
  • 9
  • 57
  • 95
  • You have to do this after the view has appeared – Jonathan. Jan 06 '20 at 23:45
  • 2
    if anyone faces the same dummy issue I did - if you're writing within ViewController, don't forget override func viewDidLayoutSubviews() { roundCorners() } – iago849 May 05 '20 at 15:22
  • What if there is no subclass? Only an outlet to uiview. – vrat2801 Jun 16 '20 at 11:31
  • In that case, you would like to override the view controller func viewWillLayoutSubviews() { super.viewWillLayoutSubviews() theView.roundCorners() }. Let me add this to the answer. – Stéphane de Luca Jun 16 '20 at 11:57
  • I use the 1st solution but its not working properly on ipad – Rana Ali Waseem Jun 30 '20 at 16:59
  • borderColor and borderWidth not effect in ios 10 when use corner [.topLeft, .topRight]. https://github.com/phucdth12a/Images/blob/master/Simulator%20Screen%20Shot%20-%20iPhone%205s%20-%202020-07-16%20at%2010.17.33.png – phú dương hiển Jul 16 '20 at 03:19
  • 1
    UIBezierPath works with fix size view, but create problem when view size change with auto layout constrains – Hardik Darji May 27 '21 at 06:28
  • It has saved me time (quick rounding of a fixed view), and cost me time (it breaks layout constraints unexpectedly). This way is causing glitches when animating the rounded view in certain cases (e.g. using animation with spring damping based). – Samuël Jun 22 '21 at 16:23
  • Huge respect, Saved my hours, was missing viewWillLayoutSubviews, Thanks – singh.jitendra Jul 20 '21 at 13:43
285

Here is a Swift version of @JohnnyRockex answer

extension UIView {

    func roundCorners(_ corners: UIRectCorner, radius: CGFloat) {
         let path = UIBezierPath(roundedRect: self.bounds, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
         let mask = CAShapeLayer()
         mask.path = path.cgPath
         self.layer.mask = mask
    }

}

view.roundCorners([.topLeft, .bottomRight], radius: 10)

Note

If you're using Auto Layout, you'll need to subclass your UIView and call roundCorners in the view's layoutSubviews for optimal effect.

class View: UIView {
    override func layoutSubviews() {
        super.layoutSubviews()

        self.roundCorners([.topLeft, .bottomLeft], radius: 10)
    }
}
Hashem Aboonajmi
  • 13,077
  • 8
  • 66
  • 75
Arbitur
  • 38,684
  • 22
  • 91
  • 128
  • 15
    Great extension. However: `.TopRight` and `.BottomRight` don't seem to work for me. – Onichan Mar 25 '15 at 10:06
  • When doesnt those edges work? When you use only one of those edges or both or all edges? Because I havent ran into this problem. – Arbitur Jul 17 '15 at 06:57
  • 4
    on *Swift 2*: `view.roundCorners([.TopLeft , .BottomLeft], radius: 10)` – fahrulazmi Sep 17 '15 at 07:53
  • This is a beautiful solution, but I can't seem to round any other corner but .TopLeft. Could this be due to AutoLayout or something? I don't know where to look, hehe. – Mattias Sep 24 '15 at 14:09
  • Can't edit my comment but I was calling the function in the wrong function. Calling it in an – Mattias Sep 24 '15 at 14:23
  • 2
    It's not working on the Right, because your view's bounds is not yet well defined with autolayout... I'm currently facing the same issue. – dosdos Oct 28 '15 at 09:50
  • 3
    @dosdos Subclass that view and in layoutSubviews round the corners of self. – Arbitur Oct 28 '15 at 10:02
  • 1
    just to make it clear, like what Arbitur said: `override func viewDidLayoutSubviews() {viewToRound.roundCorners([.TopLeft, .TopRight], radius: BUTTON_RADIUS)}` – Vic Feb 25 '16 at 10:07
  • If you are in a UIView, use `override func layoutSubviews() { super.layoutSubviews() // Round the bottom-left and top-right corners self.label.roundCorners([.BottomLeft, .TopRight], radius: CORNER_RADIUS)` } – Frederic Adda May 02 '16 at 09:30
  • 16
    If you use auto layout, and have not the reference to the view want to round, you can call `.layoutIfNeeded()`, then this method. – zgjie May 18 '16 at 08:41
  • issue => That's work perfectly! Except that my view is resizing when Im using your function :( Any ideas?? – Kevin Sabbe Feb 28 '17 at 12:46
  • not working for bottom. i.e. bottomLeft & bottomRight – user1673099 Apr 12 '17 at 06:42
  • I'm on Swift 4, use Auto-Layout, and I get a blank screen lol (view is invisible) – Van Du Tran Nov 30 '17 at 01:34
  • 1
    Calling `layoutIfNeeded()` did the trick. Just a note, if you want shadow on your view, shadow won't appear. – Van Du Tran Nov 30 '17 at 01:40
  • @Arbitur in swift 4 this is resizing the view and making it shorter. Any idea why? In `override func awakeFromNib()` I call `self.scheduledView.roundCorners([.topLeft, .topRight], radius: 3.0)` then layoutIfNeeded() – SpaceDust__ Jun 15 '18 at 15:03
  • @u.gen as I've stated in my answer. Place the code inside layoutSubViews instead. – Arbitur Jun 15 '18 at 15:06
  • awesome small solution. both topLeft and topRight are working. – Anuran Barman Jan 23 '19 at 06:08
  • It wont work if you are the view inside a table view. And also during screen rotation. – Ram Madhavan Apr 16 '20 at 13:49
102

Swift code example here: https://stackoverflow.com/a/35621736/308315


Not directly. You will have to:

  1. Create a CAShapeLayer
  2. Set its path to be a CGPathRef based on view.bounds but with only two rounded corners (probably by using +[UIBezierPath bezierPathWithRoundedRect:byRoundingCorners:cornerRadii:])
  3. Set your view.layer.mask to be the CAShapeLayer
Community
  • 1
  • 1
Kurt Revis
  • 27,695
  • 5
  • 68
  • 74
  • 24
    Be warned this will hurt performance if you do it on more than just a couple of views... – jjxtra Jun 18 '12 at 21:15
  • @jjxtra So what's the best way to do it without much performance loss? I want to show it in UITableViewCell. – xi.lin Apr 18 '16 at 02:06
  • @xi.lin from what I remember setting **layer.shouldRasterize == YES** improves speed 5 times or so. But the docs say it only works if you work with non-transparent cells. Give it a shot. – codrut Oct 09 '17 at 08:22
68

Here is a short method implemented like this:

- (void)viewDidLoad {
    [super viewDidLoad];
    UIButton *openInMaps = [UIButton new];
    [openInMaps setFrame:CGRectMake(15, 135, 114, 70)];
    openInMaps = (UIButton *)[self roundCornersOnView:openInMaps onTopLeft:NO topRight:NO bottomLeft:YES bottomRight:NO radius:5.0];
}

- (UIView *)roundCornersOnView:(UIView *)view onTopLeft:(BOOL)tl topRight:(BOOL)tr bottomLeft:(BOOL)bl bottomRight:(BOOL)br radius:(float)radius {

    if (tl || tr || bl || br) {
        UIRectCorner corner = 0;
        if (tl) {corner = corner | UIRectCornerTopLeft;}
        if (tr) {corner = corner | UIRectCornerTopRight;}
        if (bl) {corner = corner | UIRectCornerBottomLeft;}
        if (br) {corner = corner | UIRectCornerBottomRight;}

        UIView *roundedView = view;
        UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:roundedView.bounds byRoundingCorners:corner cornerRadii:CGSizeMake(radius, radius)];
        CAShapeLayer *maskLayer = [CAShapeLayer layer];
        maskLayer.frame = roundedView.bounds;
        maskLayer.path = maskPath.CGPath;
        roundedView.layer.mask = maskLayer;
        return roundedView;
    }
    return view;
}
Johnny Rockex
  • 4,136
  • 3
  • 35
  • 55
  • What about setting color ? maskLayer.borderWidth = 10; maskLayer.borderColor = [UIColor redColor].CGColor; its not working for me sir. – kiran Feb 16 '18 at 15:52
  • 1
    @kiran a mask doesn't have a colour, you could add a second CAShapeLayer if you want to have a border – Johnny Rockex Feb 20 '18 at 16:21
51

It can workes Swift 5.x also, follow my complete answers. In Swift 4.1 and Xcode 9.4.1

In iOS 11 this single line is enough:

detailsSubView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]//Set your view here

See the complete code:

//In viewDidLoad
if #available(iOS 11.0, *) {
        detailsSubView.clipsToBounds = false
        detailsSubView.layer.cornerRadius = 10
        detailsSubView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
} else {
      //For lower versions
}

But for lower versions

let rectShape = CAShapeLayer()
    rectShape.bounds = detailsSubView.frame
    rectShape.position = detailsSubView.center
    rectShape.path = UIBezierPath(roundedRect: detailsSubView.bounds,    byRoundingCorners: [.topLeft , .topRight], cornerRadii: CGSize(width: 20, height: 20)).cgPath
    detailsSubView.layer.mask = rectShape

Complete code is.

if #available(iOS 11.0, *) {
    detailsSubView.clipsToBounds = false
    detailsSubView.layer.cornerRadius = 10
    detailsSubView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
} else {
    let rectShape = CAShapeLayer()
    rectShape.bounds = detailsSubView.frame
    rectShape.position = detailsSubView.center
    rectShape.path = UIBezierPath(roundedRect: detailsSubView.bounds,    byRoundingCorners: [.topLeft , .topRight], cornerRadii: CGSize(width: 20, height: 20)).cgPath
    detailsSubView.layer.mask = rectShape
}

If you are using AutoResizing in storyboard write this code in viewDidLayoutSubviews().

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()

    if #available(iOS 11.0, *) {
        detailsSubView.clipsToBounds = false
        detailsSubView.layer.cornerRadius = 10
        detailsSubView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
    } else {
        let rectShape = CAShapeLayer()
        rectShape.bounds = detailsSubView.frame
        rectShape.position = detailsSubView.center
        rectShape.path = UIBezierPath(roundedRect: detailsSubView.bounds,    byRoundingCorners: [.topLeft , .topRight], cornerRadii: CGSize(width: 20, height: 20)).cgPath
        detailsSubView.layer.mask = rectShape
    }
}
Naresh
  • 16,698
  • 6
  • 112
  • 113
  • Any solution for ios 9? – Jaydeep Patel Feb 14 '19 at 09:25
  • @jay let rectShape = CAShapeLayer() rectShape.bounds = detailsSubView.frame rectShape.position = detailsSubView.center rectShape.path = UIBezierPath(roundedRect: detailsSubView.bounds, byRoundingCorners: [.topLeft , .topRight], cornerRadii: CGSize(width: 20, height: 20)).cgPath detailsSubView.layer.mask = rectShape This code can work for iOS 9 – Naresh Feb 14 '19 at 09:29
  • Not working for ios 9. Its there is no border on corner part. – Jaydeep Patel Feb 14 '19 at 09:41
  • @jay I don't have iOS 9 os phone, but I checked in iOS 9 simulator. It's working...How & Where you checked. Can you suggest for me to check in iOS 9 – Naresh Feb 14 '19 at 09:54
  • i checked in simulator.. here is out put https://docs.google.com/document/d/1wXu1Ooetx1QTsX7cwCmQDs0nPvsfA1MrYsSINUi64Hk/edit?usp=sharing – Jaydeep Patel Feb 14 '19 at 10:04
  • ` detailsSubView.clipsToBounds = false` should be true right ? – byJeevan Mar 09 '21 at 17:33
40

There is a super simple way of doing it. I found it on here.

view.clipsToBounds = true
view.layer.cornerRadius = 24
view.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]

It uses the stock cornerRadius property on the CALayer of a view. You just need to define the corners. layerMinXMinYCorner is top left layerMaxXMinYCorner is top right.

Neil Japhtha
  • 875
  • 10
  • 9
35

This would be the simplest answer:

yourView.layer.cornerRadius = 8
yourView.layer.masksToBounds = true
yourView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
pierre23
  • 3,846
  • 1
  • 28
  • 28
30

Swift 4 Swift 5 easy way in 1 line

Usage:

//MARK:- Corner Radius of only two side of UIViews
self.roundCorners(view: yourview, corners: [.bottomLeft, .topRight], radius: 12.0)

Function:

//MARK:- Corner Radius of only two side of UIViews
func roundCorners(view :UIView, corners: UIRectCorner, radius: CGFloat){
        let path = UIBezierPath(roundedRect: view.bounds, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
        let mask = CAShapeLayer()
        mask.path = path.cgPath
        view.layer.mask = mask
}

In Objective-C

Usage:

[self.verticalSeparatorView roundCorners:UIRectCornerTopLeft | UIRectCornerTopRight radius:10.0];

Function used in a Category (only one corner):

-(void)roundCorners: (UIRectCorner) corners radius:(CGFloat)radius {
        UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:self.bounds byRoundingCorners:corners cornerRadii:CGSizeMake(radius, radius)];
        CAShapeLayer *mask = [[CAShapeLayer alloc] init];
        mask.path = path.CGPath;
        self.layer.mask = mask;
    }
Harshil Kotecha
  • 2,846
  • 4
  • 27
  • 41
Shakeel Ahmed
  • 5,361
  • 1
  • 43
  • 34
29

iOS 11 , Swift 4
And you can try this code:

if #available(iOS 11.0, *) {
   element.clipsToBounds = true
   element.layer.cornerRadius = CORNER_RADIUS
   element.layer.maskedCorners = [.layerMaxXMaxYCorner]
} else {
   // Fallback on earlier versions
}

And you can using this in table view cell.

reza_khalafi
  • 6,230
  • 7
  • 56
  • 82
18

My solution for rounding specific corners of UIView and UITextFiels in swift is to use

.layer.cornerRadius

and

layer.maskedCorners

of actual UIView or UITextFields.

Example:

fileprivate func inputTextFieldStyle() {
        inputTextField.layer.masksToBounds = true
        inputTextField.layer.borderWidth = 1
        inputTextField.layer.cornerRadius = 25
        inputTextField.layer.maskedCorners = [.layerMaxXMaxYCorner,.layerMaxXMinYCorner]
        inputTextField.layer.borderColor = UIColor.white.cgColor
    }

And by using

.layerMaxXMaxYCorner

and

.layerMaxXMinYCorner

, I can specify top right and bottom right corner of the UITextField to be rounded.

You can see the result here:

enter image description here

Bane M
  • 288
  • 3
  • 7
17

Emma: .TopRight and .BottomRight are not working for you perhaps because the call to view.roundCorners is done BEFORE final view bounds are calculated. Note that the Bezier Path derives from the view bounds at the time it is called. For example, if auto layout will narrow the view, the round corners on the right side might be outside the view. Try to call it in viewDidLayoutSubviews, where the view's bound is final.

Benjamin RD
  • 11,516
  • 14
  • 87
  • 157
David Barta
  • 336
  • 3
  • 7
  • Thank you. For me, what solved the issue was moving the call to the implementation given by "Arbitur" from viewDidLoad and viewWillAppear to viewDidAppear. I could do so since my control is initially set to hidden. – Matti Aug 26 '15 at 07:30
  • You are right. It will create issue with autolayout and you have done good job. – Ashish Kakkad Jan 12 '16 at 11:57
17

Try this code,

UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:view.bounds byRoundingCorners:( UIRectCornerTopLeft | UIRectCornerTopRight) cornerRadii:CGSizeMake(5.0, 5.0)];

CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
maskLayer.frame = self.view.bounds;
maskLayer.path  = maskPath.CGPath;

view.layer.mask = maskLayer;
12

Simple extension

extension UIView {
    func roundCorners(corners: UIRectCorner, radius: CGFloat) {
        if #available(iOS 11, *) {
            self.clipsToBounds = true
            self.layer.cornerRadius = radius
            var masked = CACornerMask()
            if corners.contains(.topLeft) { masked.insert(.layerMinXMinYCorner) }
            if corners.contains(.topRight) { masked.insert(.layerMaxXMinYCorner) }
            if corners.contains(.bottomLeft) { masked.insert(.layerMinXMaxYCorner) }
            if corners.contains(.bottomRight) { masked.insert(.layerMaxXMaxYCorner) }
            self.layer.maskedCorners = masked
        }
        else {
            let path = UIBezierPath(roundedRect: bounds, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
            let mask = CAShapeLayer()
            mask.path = path.cgPath
            layer.mask = mask
        }
    }
}

Usage:

view.roundCorners(corners: [.bottomLeft, .bottomRight], radius: 12)
FunkyKat
  • 3,233
  • 1
  • 23
  • 20
11

Swift 4

extension UIView {

    func roundTop(radius:CGFloat = 5){
        self.clipsToBounds = true
        self.layer.cornerRadius = radius
        if #available(iOS 11.0, *) {
            self.layer.maskedCorners = [.layerMaxXMinYCorner, .layerMinXMinYCorner]
        } else {
            // Fallback on earlier versions
        }
    }

    func roundBottom(radius:CGFloat = 5){
        self.clipsToBounds = true
        self.layer.cornerRadius = radius
        if #available(iOS 11.0, *) {
            self.layer.maskedCorners = [.layerMaxXMaxYCorner, .layerMinXMaxYCorner]
        } else {
            // Fallback on earlier versions
        }
    }
}
Benjamin RD
  • 11,516
  • 14
  • 87
  • 157
  • You saved my day in my case I had to make the table view rounded for particular cells – Shakti Apr 05 '19 at 14:05
  • Perfect ans according my requirement. I used all the answer, nothing worked for me, but your answer helped me and solved my problem. – Nikita Patil Oct 24 '19 at 07:13
9

Use this extension, it'll cover everything.

extension UIView {

   func roundTopCorners(radius: CGFloat = 10) {
    
       self.clipsToBounds = true
       self.layer.cornerRadius = radius
       if #available(iOS 11.0, *) {
           self.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
       } else {
           self.roundCorners(corners: [.topLeft, .topRight], radius: radius)
       }
   }

   func roundBottomCorners(radius: CGFloat = 10) {
    
       self.clipsToBounds = true
       self.layer.cornerRadius = radius
       if #available(iOS 11.0, *) {
           self.layer.maskedCorners = [.layerMinXMaxYCorner, .layerMaxXMaxYCorner]
       } else {
           self.roundCorners(corners: [.bottomLeft, .bottomRight], radius: radius)
       }
   }

   private func roundCorners(corners: UIRectCorner, radius: CGFloat) {
    
        let path = UIBezierPath(roundedRect: bounds, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
        let mask = CAShapeLayer()
        mask.path = path.cgPath
        layer.mask = mask
    }
}

and then use it like this:-

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
            
    self.yourView.roundTopCorners()
}

Note:- I'll recommend you to don't put this code inside viewDidLayoutSubviews(), because whenever view updates, you'll get call inside it. So use viewDidAppear(), it'll work like a charm.

Vipul Kumar
  • 893
  • 9
  • 19
7

Here is best way Swift 5:

import UIKit

extension UIView {

func roundCorners(radius: CGFloat = 10, corners: UIRectCorner = .allCorners) {
        self.clipsToBounds = true
        self.layer.cornerRadius = radius
        if #available(iOS 11.0, *) {
            var arr: CACornerMask = []
            
            let allCorners: [UIRectCorner] = [.topLeft, .topRight, .bottomLeft, .bottomRight, .allCorners]
            
            for corn in allCorners {
                if(corners.contains(corn)){
                    switch corn {
                    case .topLeft:
                        arr.insert(.layerMinXMinYCorner)
                    case .topRight:
                        arr.insert(.layerMaxXMinYCorner)
                    case .bottomLeft:
                        arr.insert(.layerMinXMaxYCorner)
                    case .bottomRight:
                        arr.insert(.layerMaxXMaxYCorner)
                    case .allCorners:
                        arr.insert(.layerMinXMinYCorner)
                        arr.insert(.layerMaxXMinYCorner)
                        arr.insert(.layerMinXMaxYCorner)
                        arr.insert(.layerMaxXMaxYCorner)
                    default: break
                    }
                }
            }
            self.layer.maskedCorners = arr
        } else {
            self.roundCornersBezierPath(corners: corners, radius: radius)
        }
    }
    
    private func roundCornersBezierPath(corners: UIRectCorner, radius: CGFloat) {
        let path = UIBezierPath(roundedRect: bounds, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
        let mask = CAShapeLayer()
        mask.path = path.cgPath
        layer.mask = mask
    }
    
}
6

A way to do this programmatically would be to create a UIView over the top part of the UIView that has the rounded corners. Or you could hide the top underneath something.

Nate Symer
  • 2,185
  • 1
  • 20
  • 27
6
    // Create the path (with only the top-left corner rounded)
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:view.bounds 
                           byRoundingCorners:(UIRectCornerBottomLeft | UIRectCornerBottomRight) 
                           cornerRadii:CGSizeMake(7.0, 7.0)];

// Create the shape layer and set its path
CAShapeLayer *maskLayer = [CAShapeLayer layer];
maskLayer.frame = cell.stripBlackImnageView.bounds;
maskLayer.path = maskPath.CGPath; 
// Set the newly created shapelayer as the mask for the image view's layer
view.layer.mask = maskLayer;
silwar
  • 6,470
  • 3
  • 46
  • 66
Waseem Sarwar
  • 2,645
  • 1
  • 21
  • 18
4

The easiest way would be to make a mask with a rounded corner layer.

CALayer *maskLayer = [CALayer layer];
maskLayer.frame = CGRectMake(0,0,maskWidth ,maskHeight);
maskLayer.contents = (__bridge id)[[UIImage imageNamed:@"maskImageWithRoundedCorners.png"] CGImage];

aUIView.layer.mask = maskLayer;

And don't forget to:

#import <QuartzCore/QuartzCore.h>
Matt Hudson
  • 7,329
  • 5
  • 49
  • 66
4

All of the answers already given are really good and valid (especially Yunus idea of using the mask property).

However I needed something a little more complex because my layer could often change sizes which mean I needed to call that masking logic every time and this was a little bit annoying.

I used swift extensions and computed properties to build a real cornerRadii property which takes care of auto updating the mask when layer is layed out.

This was achieved using Peter Steinberg great Aspects library for swizzling.

Full code is here:

extension CALayer {
  // This will hold the keys for the runtime property associations
  private struct AssociationKey {
    static var CornerRect:Int8 = 1    // for the UIRectCorner argument
    static var CornerRadius:Int8 = 2  // for the radius argument
  }

  // new computed property on CALayer
  // You send the corners you want to round (ex. [.TopLeft, .BottomLeft])
  // and the radius at which you want the corners to be round
  var cornerRadii:(corners: UIRectCorner, radius:CGFloat) {
    get {
      let number = objc_getAssociatedObject(self, &AssociationKey.CornerRect)  as? NSNumber ?? 0
      let radius = objc_getAssociatedObject(self, &AssociationKey.CornerRadius)  as? NSNumber ?? 0
      return (corners: UIRectCorner(rawValue: number.unsignedLongValue), radius: CGFloat(radius.floatValue))
    }
    set (v) {
      let radius = v.radius
      let closure:((Void)->Void) = {
        let path = UIBezierPath(roundedRect: self.bounds, byRoundingCorners: v.corners, cornerRadii: CGSize(width: radius, height: radius))
        let mask = CAShapeLayer()
        mask.path = path.CGPath
        self.mask = mask
      }
      let block: @convention(block) Void -> Void = closure
      let objectBlock = unsafeBitCast(block, AnyObject.self)
      objc_setAssociatedObject(self, &AssociationKey.CornerRect, NSNumber(unsignedLong: v.corners.rawValue), .OBJC_ASSOCIATION_RETAIN)
      objc_setAssociatedObject(self, &AssociationKey.CornerRadius, NSNumber(float: Float(v.radius)), .OBJC_ASSOCIATION_RETAIN)
      do { try aspect_hookSelector("layoutSublayers", withOptions: .PositionAfter, usingBlock: objectBlock) }
      catch _ { }
    }
  }
}

I wrote a simple blog post explaining this.

apouche
  • 9,703
  • 6
  • 40
  • 45
3

A lovely extension to reuse Yunus Nedim Mehel solution

Swift 2.3

extension UIView {
func roundCornersWithLayerMask(cornerRadii: CGFloat, corners: UIRectCorner) {
    let path = UIBezierPath(roundedRect: bounds,
                            byRoundingCorners: corners,
                            cornerRadii: CGSize(width: cornerRadii, height: cornerRadii))
    let maskLayer = CAShapeLayer()
    maskLayer.path = path.CGPath
    layer.mask = maskLayer
} }

Usage

let view = UIView()
view.roundCornersWithLayerMask(10,[.TopLeft,.TopRight])
apinho
  • 2,235
  • 3
  • 25
  • 39
3

If you're looking for an interface builder only solution there is one for iOS 11 and higher. See my answer here: https://stackoverflow.com/a/58626264

Bruno Bieri
  • 9,724
  • 11
  • 63
  • 92
3

Use this extension for set corner round and round border with round corners

use like this :

override func viewWillLayoutSubviews() {
            super.viewWillLayoutSubviews()

         myView.roundCornersWithBorder(corners: [.topLeft, .topRight], radius: 8.0)
    
        myView.roundCorners(corners: [.topLeft, .topRight], radius: 8.0)

        }



extension UIView {
   func roundCorners(corners: UIRectCorner, radius: CGFloat) {
        let path = UIBezierPath(roundedRect: bounds, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
        let mask = CAShapeLayer()
        mask.path = path.cgPath
        layer.mask = mask
    }
    
    func roundCornersWithBorder(corners: UIRectCorner, radius: CGFloat) {
        let maskLayer = CAShapeLayer()
        maskLayer.frame = bounds
        maskLayer.path = UIBezierPath(roundedRect: bounds, byRoundingCorners: [.topLeft, .topRight], cornerRadii: CGSize(width: radius, height: radius)).cgPath
        
        layer.mask = maskLayer
        
        // Add border
        let borderLayer = CAShapeLayer()
        borderLayer.path = maskLayer.path // Reuse the Bezier path
        borderLayer.fillColor = UIColor.clear.cgColor
        borderLayer.strokeColor = UIColor(red:3/255, green:33/255, blue:70/255, alpha: 0.15).cgColor
        borderLayer.lineWidth = 2
        borderLayer.frame = bounds
        layer.addSublayer(borderLayer)
    }
    
}
2

This is how you can set a corner radius for each corner of a button with Xamarin in C#:

var maskPath = UIBezierPath.FromRoundedRect(MyButton.Bounds, UIRectCorner.BottomLeft | UIRectCorner.BottomRight,
    new CGSize(10.0, 10.0));
var maskLayer = new CAShapeLayer
{
    Frame = MyButton.Bounds,
    Path = maskPath.CGPath
};
MyButton.Layer.Mask = maskLayer;
jfmg
  • 2,626
  • 1
  • 24
  • 32
2

For SwiftUI

I found these solutions you can check from here https://stackoverflow.com/a/56763282/3716103

I highly recommend the first one

Option 1: Using Path + GeometryReader

(more info on GeometryReader: https://swiftui-lab.com/geometryreader-to-the-rescue/)

struct ContentView : View {
    var body: some View {

        Text("Hello World!")
            .foregroundColor(.white)
            .font(.largeTitle)
            .padding(20)
            .background(RoundedCorners(color: .blue, tl: 0, tr: 30, bl: 30, br: 0))
    }
}

give corner radius to text view using background

RoundedCorners

struct RoundedCorners: View {

    var color: Color = .white

    var tl: CGFloat = 0.0
    var tr: CGFloat = 0.0
    var bl: CGFloat = 0.0
    var br: CGFloat = 0.0

    var body: some View {
        GeometryReader { geometry in
            Path { path in

                let w = geometry.size.width
                let h = geometry.size.height

                // Make sure we do not exceed the size of the rectangle
                let tr = min(min(self.tr, h/2), w/2)
                let tl = min(min(self.tl, h/2), w/2)
                let bl = min(min(self.bl, h/2), w/2)
                let br = min(min(self.br, h/2), w/2)

                path.move(to: CGPoint(x: w / 2.0, y: 0))
                path.addLine(to: CGPoint(x: w - tr, y: 0))
                path.addArc(center: CGPoint(x: w - tr, y: tr), radius: tr, startAngle: Angle(degrees: -90), endAngle: Angle(degrees: 0), clockwise: false)
                path.addLine(to: CGPoint(x: w, y: h - be))
                path.addArc(center: CGPoint(x: w - br, y: h - br), radius: br, startAngle: Angle(degrees: 0), endAngle: Angle(degrees: 90), clockwise: false)
                path.addLine(to: CGPoint(x: bl, y: h))
                path.addArc(center: CGPoint(x: bl, y: h - bl), radius: bl, startAngle: Angle(degrees: 90), endAngle: Angle(degrees: 180), clockwise: false)
                path.addLine(to: CGPoint(x: 0, y: tl))
                path.addArc(center: CGPoint(x: tl, y: tl), radius: tl, startAngle: Angle(degrees: 180), endAngle: Angle(degrees: 270), clockwise: false)
            }
            .fill(self.color)
        }
    }
}

RoundedCorners_Previews

struct RoundedCorners_Previews: PreviewProvider {
    static var previews: some View {
        RoundedCorners(color: .pink, tl: 40, tr: 40, bl: 40, br: 40)
    }
}

I give the corner radius to the top of view only

2

Make View TopLeft and TopRight CornerRadius ONLY

lazy var footerBackgroundView: UIView = {

    let view = UIView()
    view.translatesAutoresizingMaskIntoConstraints = false
    view.backgroundColor = .blue

    //Make Corners Radius TopLeft & Top Right
    view.clipsToBounds = true
    view.layer.cornerRadius = 8
    view.layer.maskedCorners = [.layerMaxXMinYCorner, .layerMinXMinYCorner] 
    
    return view
}()
Chithian
  • 253
  • 3
  • 8
1

After change bit of code @apinho In swift 4.3 working fine

extension UIView {
func roundCornersWithLayerMask(cornerRadii: CGFloat, corners: UIRectCorner) {
    let path = UIBezierPath(roundedRect: bounds,
                            byRoundingCorners: corners,
                            cornerRadii: CGSize(width: cornerRadii, height: cornerRadii))
    let maskLayer = CAShapeLayer()
    maskLayer.path = path.cgPath
    layer.mask = maskLayer
  }
}

To use this function for you view

YourViewName. roundCornersWithLayerMask(cornerRadii: 20,corners: [.topLeft,.topRight])
Sanjay Mishra
  • 672
  • 7
  • 17
0

Another version of Stephane's answer.

import UIKit

    class RoundCornerView: UIView {
    var corners : UIRectCorner = [.topLeft,.topRight,.bottomLeft,.bottomRight]
        var roundCornerRadius : CGFloat = 0.0
        override func layoutSubviews() {
            super.layoutSubviews()
            if corners.rawValue > 0 && roundCornerRadius > 0.0 {
                self.roundCorners(corners: corners, radius: roundCornerRadius)
            }
        }
        private func roundCorners(corners: UIRectCorner, radius: CGFloat) {
            let path = UIBezierPath(roundedRect: bounds, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
            let mask = CAShapeLayer()
            mask.path = path.cgPath
            layer.mask = mask
        }

    }
mehdi
  • 2,703
  • 1
  • 13
  • 15
0

In Swift 4.2, Create it via @IBDesignable like this:

@IBDesignable

class DesignableViewCustomCorner: UIView {

    @IBInspectable var cornerRadious: CGFloat = 0 {
        didSet {
            let path = UIBezierPath(roundedRect: self.bounds, byRoundingCorners: [.topLeft, .topRight], cornerRadii: CGSize(width: cornerRadious, height: cornerRadious))
            let mask = CAShapeLayer()
            mask.path = path.cgPath
            self.layer.mask = mask
        }
    }

}
Sreeraj VR
  • 1,524
  • 19
  • 35