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.
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)
}
}
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.
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];
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)
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)
iOS 11+ Only | You can check iOS usage stats here
Since the CACornerMask
rawValue is an UInt
you know that a CACornerMask
rawValue is the sum of each CACornerMask.Element
rawValue
More specifically:
layerMinXMinYCorner
) = 1 layerMaxXMinYCorner
) = 2 layerMinXMaxYCorner
) = 4layerMaxXMaxYCorner
) = 8 So for example if you want top left and top right corners you can just type CACornerMask(rawValue: 3)
.
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])
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))
Please note that syntax/systems have changed a lot since this question was asked a long time ago!
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:
let path = UIBezierPath(
roundedRect: self.bounds,
byRoundingCorners: [.topLeft,.topRight],
cornerRadii: CGSize(width: r, height: r))
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
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)
}
}
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)
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:
view.layer.cornerRadius = 10.0
view.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMaxYCorner, .layerMinXMaxYCorner]
Best way to do it!
Swift 5: For top-left and top-right round corners.
yourView.layer.cornerRadius = 12
yourView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
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
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
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
Placement of Yellow View will decide, which 2 corners will not be rounded. Hope this is easy to implement.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.