Create a rectangle with just two rounded corners in swift?

IosSwiftCore Graphics

Ios Problem Overview


I need to create a rectangle that have just two rounded corners in swift (Objective C code also ok).

At the moment my code is creating two rectangles with

CGPathCreateWithRoundedRect(CGRectMake(0, 0, 30, 60), 5, 5, nil);

and

CGPathCreateWithRoundedRect(CGRectMake(0, 0, 30, 60), 0, 0, nil);

and merging them (to have two right angle corners and two rounded ones) but I am not happy with the code and I am pretty sure there should be much better ways to do it.

I am new to iOS and graphical development and swift.

Ios Solutions


Solution 1 - Ios

Update: See this answer below for Swift 4 / iOS 11 which is much, much easier


Here's a quick Swift 3 extension you can use to do rounding and optional borders.

Note: if you're using autolayout, you may need to call this in one of the view lifecycle callbacks like viewDidLayoutSubviews or layoutSubviews after the view has been constrained.

import UIKit

extension UIView {
    
    /**
     Rounds the given set of corners to the specified radius
     
     - parameter corners: Corners to round
     - parameter radius:  Radius to round to
     */
    func round(corners: UIRectCorner, radius: CGFloat) {
        _ = _round(corners: corners, radius: radius)
    }
    
    /**
     Rounds the given set of corners to the specified radius with a border
     
     - parameter corners:     Corners to round
     - parameter radius:      Radius to round to
     - parameter borderColor: The border color
     - parameter borderWidth: The border width
     */
    func round(corners: UIRectCorner, radius: CGFloat, borderColor: UIColor, borderWidth: CGFloat) {
        let mask = _round(corners: corners, radius: radius)
        addBorder(mask: mask, borderColor: borderColor, borderWidth: borderWidth)
    }
    
    /**
     Fully rounds an autolayout view (e.g. one with no known frame) with the given diameter and border
     
     - parameter diameter:    The view's diameter
     - parameter borderColor: The border color
     - parameter borderWidth: The border width
     */
    func fullyRound(diameter: CGFloat, borderColor: UIColor, borderWidth: CGFloat) {
        layer.masksToBounds = true
        layer.cornerRadius = diameter / 2
        layer.borderWidth = borderWidth
        layer.borderColor = borderColor.cgColor;
    }
    
}

private extension UIView {
    
    @discardableResult func _round(corners: UIRectCorner, radius: CGFloat) -> CAShapeLayer {
        let path = UIBezierPath(roundedRect: bounds, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
        let mask = CAShapeLayer()
        mask.path = path.cgPath
        self.layer.mask = mask
        return mask
    }
    
    func addBorder(mask: CAShapeLayer, borderColor: UIColor, borderWidth: CGFloat) {
        let borderLayer = CAShapeLayer()
        borderLayer.path = mask.path
        borderLayer.fillColor = UIColor.clear.cgColor
        borderLayer.strokeColor = borderColor.cgColor
        borderLayer.lineWidth = borderWidth
        borderLayer.frame = bounds
        layer.addSublayer(borderLayer)
    }
    
}

Solution 2 - Ios

Swift 4+, iOS 11+

If you already have a UIView named myView referenced as an IBOutlet, try adding the following two lines in ViewDidLoad() or wherever it's being loaded:

myView.layer.cornerRadius = 10
myView.layer.maskedCorners = [.layerMinXMaxYCorner, .layerMaxXMaxYCorner]

You can change the array [] to any combination of MinX, MinY, MaxX, and MaxY to select the desired corners. The above example rounds the bottom two corners.

This is just another approach, can be a bit simpler depending on your design.

Solution 3 - Ios

In Swift 2.3 you could do so by

let maskPath = UIBezierPath(roundedRect: anyView.bounds,
            byRoundingCorners: [.BottomLeft, .BottomRight],
            cornerRadii: CGSize(width: 10.0, height: 10.0))

let shape = CAShapeLayer()
shape.path = maskPath.CGPath
view.layer.mask = shape

In Objective-C you could use the UIBezierPath class method

bezierPathWithRoundedRect:byRoundingCorners:cornerRadii:

example implementation-

// set the corner radius to the specified corners of the passed container
- (void)setMaskTo:(UIView*)view byRoundingCorners:(UIRectCorner)corners
{
    UIBezierPath *rounded = [UIBezierPath bezierPathWithRoundedRect:view.bounds
                                                  byRoundingCorners:corners
                                                        cornerRadii:CGSizeMake(10.0, 10.0)];
    CAShapeLayer *shape = [[CAShapeLayer alloc] init];
    [shape setPath:rounded.CGPath];
    view.layer.mask = shape;
}

and call the above method as-

[self setMaskTo:anyView byRoundingCorners:UIRectCornerBottomLeft | UIRectCornerBottomRight];

Solution 4 - Ios

Swift 3 - Useful UIView extension when you need to round specific corners of some views:

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

then just use it like this:

someView.round(corners: [.topLeft, .topRight], radius: 5)

Solution 5 - Ios

Building on top of Sanjay's excellent answer, I wrote a quick CALayer extension for Swift 2.3, in case you need to do this sort of "only round some corners" thing more than once.

extension CALayer {
  func roundCorners(corners: UIRectCorner, radius: CGFloat) {
    let maskPath = UIBezierPath(roundedRect: bounds,
                                byRoundingCorners: corners,
                                cornerRadii: CGSize(width: radius, height: radius))

    let shape = CAShapeLayer()
    shape.path = maskPath.CGPath
    mask = shape
  }
}

Usage:

myView.layer.roundCorners([.TopLeft, .TopRight], radius: myCornerRadius)

Swift 3.0 (In this example the bounds came from the view not from the layer. Using the bounds from the view make this code to work with views in a UITableViewCell.):

func roundCorners(corners: UIRectCorner, radius: CGFloat, viewBounds: CGRect) {
    
    let maskPath = UIBezierPath(roundedRect: viewBounds,
                                byRoundingCorners: corners,
                                cornerRadii: CGSize(width: radius, height: radius))
    
    let shape = CAShapeLayer()
    shape.path = maskPath.cgPath
    mask = shape
}

Usage:

myView.layer.roundCorners(corners: [.topLeft, .topRight], radius: myCornerRadius, viewBounds: bounds)

Solution 6 - Ios

> iOS 11+ Only | You can check iOS usage stats here

Explanation

Since the CACornerMask rawValue is an UInt you know that a CACornerMask rawValue is the sum of each CACornerMask.Element rawValue

More specifically:

  • TopLeft (layerMinXMinYCorner) = 1
  • TopRight (layerMaxXMinYCorner) = 2
  • BottomLeft (layerMinXMaxYCorner) = 4
  • BottomRight (layerMaxXMaxYCorner) = 8

So for example if you want top left and top right corners you can just type CACornerMask(rawValue: 3).


 Example

Below is a simple extension of UIView

extension UIView {
    enum Corner:Int {
        case bottomRight = 0,
        topRight,
        bottomLeft,
        topLeft
    }
    
    private func parseCorner(corner: Corner) -> CACornerMask.Element {
        let corners: [CACornerMask.Element] = [.layerMaxXMaxYCorner, .layerMaxXMinYCorner, .layerMinXMaxYCorner, .layerMinXMinYCorner]
        return corners[corner.rawValue]
    }
    
    private func createMask(corners: [Corner]) -> UInt {
        return corners.reduce(0, { (a, b) -> UInt in
            return a + parseCorner(corner: b).rawValue
        })
    }
    
    func roundCorners(corners: [Corner], amount: CGFloat = 5) {
        layer.cornerRadius = amount
        let maskedCorners: CACornerMask = CACornerMask(rawValue: createMask(corners: corners))
        layer.maskedCorners = maskedCorners
    }
}

You can use it this like:

let myRect = UIView(frame: CGRect(x: 0, y: 0, width: 200, height: 50))
myRect.roundCorners(corners: [.topRight, .topLeft])

Solution 7 - Ios

Here is what you do in Swift 2.0

var maskPath = UIBezierPath(roundedRect: anyView.bounds,
        byRoundingCorners: [.BottomLeft, .BottomRight],
        cornerRadii: CGSize(width: 10.0, height: 10.0))

Solution 8 - Ios

Up-to-date for 2021 ...

Please note that syntax/systems have changed a lot since this question was asked a long time ago!

enter image description here

import UIKit
@IBDesignable
class RoundedEnds: UIView {
    
    override func layoutSubviews() {
        super.layoutSubviews()
        setup()
    }
    
    func setup() {
        let r = self.bounds.size.height / 2
        let path = UIBezierPath(roundedRect: self.bounds, cornerRadius:r)
        let mask = CAShapeLayer()
        mask.path = path.cgPath
        self.layer.mask = mask
    }
}

For only some corners, just change the path line of code to:

enter image description here

	let path = UIBezierPath(
		roundedRect: self.bounds,
		byRoundingCorners: [.topLeft,.topRight],
		cornerRadii: CGSize(width: r, height: r))

Solution 9 - Ios

Swift 4:

let maskPath = UIBezierPath(
            roundedRect: view.bounds,
            byRoundingCorners: [.allCorners],
            cornerRadii: CGSize(width: 10.0, height: 10.0)
        )
        
let shape = CAShapeLayer()
shape.path = maskPath.cgPath

view.layer.mask = shape

Solution 10 - Ios

Updated iWasRobbed's answer to work with the Swift 3.0 GM version:

import UIKit

extension UIView {

    /**
     Rounds the given set of corners to the specified radius

     - parameter corners: Corners to round
     - parameter radius:  Radius to round to
     */
    func round(corners: UIRectCorner, radius: CGFloat) {
        _round(corners: corners, radius: radius)
    }

    /**
     Rounds the given set of corners to the specified radius with a border

     - parameter corners:     Corners to round
     - parameter radius:      Radius to round to
     - parameter borderColor: The border color
     - parameter borderWidth: The border width
     */
    func round(corners: UIRectCorner, radius: CGFloat, borderColor: UIColor, borderWidth: CGFloat) {
        let mask = _round(corners: corners, radius: radius)
        addBorder(mask: mask, borderColor: borderColor, borderWidth: borderWidth)
    }

    /**
     Fully rounds an autolayout view (e.g. one with no known frame) with the given diameter and border

     - parameter diameter:    The view's diameter
     - parameter borderColor: The border color
     - parameter borderWidth: The border width
     */
    func fullyRound(diameter: CGFloat, borderColor: UIColor, borderWidth: CGFloat) {
        layer.masksToBounds = true
        layer.cornerRadius = diameter / 2
        layer.borderWidth = borderWidth
        layer.borderColor = borderColor.cgColor;
    }

}

private extension UIView {

    @discardableResult func _round(corners: UIRectCorner, radius: CGFloat) -> CAShapeLayer {
        let path = UIBezierPath(roundedRect: bounds, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
        let mask = CAShapeLayer()
        mask.path = path.cgPath
        self.layer.mask = mask
        return mask
    }

    func addBorder(mask: CAShapeLayer, borderColor: UIColor, borderWidth: CGFloat) {
        let borderLayer = CAShapeLayer()
        borderLayer.path = mask.path
        borderLayer.fillColor = UIColor.clear.cgColor
        borderLayer.strokeColor = borderColor.cgColor
        borderLayer.lineWidth = borderWidth
        borderLayer.frame = bounds
        layer.addSublayer(borderLayer)
    }
    
}

Solution 11 - Ios

extension CACornerMask {

    public static var leftBottom     : CACornerMask { get { return .layerMinXMaxYCorner}}
    public static var rightBottom    : CACornerMask { get { return .layerMaxXMaxYCorner}}
    public static var leftTop        : CACornerMask { get { return .layerMaxXMinYCorner}}
    public static var rightTop       : CACornerMask { get { return .layerMinXMinYCorner}}
}

extension CALayer {

    func roundCorners(_ mask:CACornerMask,corner:CGFloat) {
        self.maskedCorners = mask
        self.cornerRadius = corner
    }
}

self.viewBack.layer.roundCorners([.leftBottom,.rightBottom], corner: 23)

Solution 12 - Ios

In summary, you can create pretty extension like this:

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

Use it like this:

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

Here is all corners values:

  • .topLeft
  • .topRight
  • .bottomLeft
  • .bottomRight

Solution 13 - Ios

view.layer.cornerRadius = 10.0
view.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMaxYCorner, .layerMinXMaxYCorner]

> Best way to do it!

Solution 14 - Ios

Swift 5: For top-left and top-right round corners.

yourView.layer.cornerRadius = 12
yourView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]

Solution 15 - Ios

Objective-C version of iWasRobbed's answer:

UIView+RoundCorners.h

#import <UIKit/UIKit.h>

@interface UIView (RoundCorners)

/**
 Rounds the given set of corners to the specified radius
 
 - parameter corners: Corners to round
 - parameter radius:  Radius to round to
 */
- (void)roundCorners:(UIRectCorner)corners radius:(CGFloat)radius;

/**
 Rounds the given set of corners to the specified radius with a border
 
 - parameter corners:     Corners to round
 - parameter radius:      Radius to round to
 - parameter borderColor: The border color
 - parameter borderWidth: The border width
 */
- (void)roundCorners:(UIRectCorner)corners radius:(CGFloat)radius borderColor:(UIColor *)borderColor borderWidth:(CGFloat)borderWidth;

/**
 Fully rounds an autolayout view (e.g. one with no known frame) with the given diameter and border
 
 - parameter diameter:    The view's diameter
 - parameter borderColor: The border color
 - parameter borderWidth: The border width
 */
- (void)fullyRoundWithDiameter:(CGFloat)diameter borderColor:(UIColor *)borderColor borderWidth:(CGFloat)borderWidth;

@end

UIView+RoundCorners.m

#import "UIView+RoundCorners.h"

@implementation UIView (RoundCorners)

- (void)roundCorners:(UIRectCorner)corners radius:(CGFloat)radius {
    [self _roundCorners:corners radius:radius];
}

- (void)roundCorners:(UIRectCorner)corners radius:(CGFloat)radius borderColor:(UIColor *)borderColor borderWidth:(CGFloat)borderWidth {
    CAShapeLayer *mask = [self _roundCorners:corners radius:radius];
    [self addBorderWithMask:mask borderColor:borderColor borderWidth:borderWidth];
}

- (void)fullyRoundWithDiameter:(CGFloat)diameter borderColor:(UIColor *)borderColor borderWidth:(CGFloat)borderWidth {
    self.layer.masksToBounds = YES;
    self.layer.cornerRadius = diameter / 2;
    self.layer.borderWidth = borderWidth;
    self.layer.borderColor = borderColor.CGColor;
}

- (CAShapeLayer *)_roundCorners:(UIRectCorner)corners radius:(CGFloat)radius {
    UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:self.bounds byRoundingCorners:corners cornerRadii:CGSizeMake(radius, radius)];
    CAShapeLayer *mask = [CAShapeLayer layer];
    mask.path = path.CGPath;
    self.layer.mask = mask;
    return mask;
}

- (void)addBorderWithMask:(CAShapeLayer *)mask borderColor:(UIColor *)borderColor borderWidth:(CGFloat)borderWidth {
    CAShapeLayer *borderLayer = [CAShapeLayer layer];
    borderLayer.path = mask.path;
    borderLayer.fillColor = UIColor.clearColor.CGColor;
    borderLayer.strokeColor = borderColor.CGColor;
    borderLayer.lineWidth = borderWidth;
    borderLayer.frame = self.bounds;
    [self.layer addSublayer:borderLayer];
}

@end

Solution 16 - Ios

One simple hack could be as following. Take views like below example in image. Red View will have rounded corners and Yellow View (inside Red View) will prevent the corners to be rounded

enter image description here

Now write below code for Red View.

        self.myView.layer.cornerRadius = 15

Make sure you don't write any code as clipsToBounds = true or masksToBounds = true.

Below image is the result

enter image description here

Placement of Yellow View will decide, which 2 corners will not be rounded. Hope this is easy to implement.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMaralcView Question on Stackoverflow
Solution 1 - IosiwasrobbedView Answer on Stackoverflow
Solution 2 - IoswcarhartView Answer on Stackoverflow
Solution 3 - IosSanjay MohnaniView Answer on Stackoverflow
Solution 4 - IosbudiDinoView Answer on Stackoverflow
Solution 5 - IosEricView Answer on Stackoverflow
Solution 6 - IosFedericoView Answer on Stackoverflow
Solution 7 - IosAndyView Answer on Stackoverflow
Solution 8 - IosFattieView Answer on Stackoverflow
Solution 9 - IosBogdan BystritskiyView Answer on Stackoverflow
Solution 10 - IoscjnevinView Answer on Stackoverflow
Solution 11 - IosMohammad AkbariView Answer on Stackoverflow
Solution 12 - IosArthur StepanovView Answer on Stackoverflow
Solution 13 - IosA RView Answer on Stackoverflow
Solution 14 - IosAli RazaView Answer on Stackoverflow
Solution 15 - IosAndrei RadulescuView Answer on Stackoverflow
Solution 16 - IosTeena nath PaulView Answer on Stackoverflow