Create a rectangle with just two rounded corners in swift?
Solution 1
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 2
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 3
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 4
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
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)
Maralc
Updated on July 30, 2022Comments
-
Maralc almost 2 years
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.
-
Maralc about 9 yearsI could solve my problem with your answer. But just curious as I was trying to use the UIBezierPath.addArcWithCenter to draw the rounded corners myself and the start and end angle doesn't match at all the ones documented on developer.apple.com/library/ios/documentation/UIKit/Reference/…
-
Sanjay Mohnani about 9 yearsare you asking about using start and end angles?
-
Sanjay Mohnani about 9 years#define RADIANS(degrees) ((degrees) / (180.0 / M_PI))
-
Sanjay Mohnani about 9 yearsand use as - double startAngle = RADIANS(45); double endAngle = RADIANS(135);
-
Sanjay Mohnani about 9 yearsperfect, it's definitely usable in swift as #define in objective-c
-
Andy over 8 yearshow do you do this in swift 2.0? the "|" doesn't appear to work.
-
Onichan about 8 yearsI'm using this on a UITextField but it alters the width of the text field. Why does it change the width (which was set using Autolayout).
-
Onichan about 8 yearsUpdate: just had to call the extension in
viewDidLayoutSubviews
-
iwasrobbed about 8 years@Onichan I added a
fullyRound
method that works for autolayout views as well since the frame isn't setup if called fromviewDidLoad
code -
iwasrobbed almost 8 years@DaveG Works with any UIView subclass (such as UIButton, UILabel, UITextField, etc)
-
JSBach over 7 yearssth wrong with the bottom using as [.bottomLeft, .topLeft] for radius
-
iwasrobbed over 7 years@Dincer Looks normal to me. Playground: gist.github.com/iwasrobbed/6ebd47d64b921ca27809adaafb4cce83 / imgur.com/xZCZmO4 ... Could you be more specific please?
-
Fattie over 7 years(good one - as usual there have been many small changes in Swift, eg capitalization of constants, etc)
-
lifewithelliott over 7 years@iwasrobbed I am calling the round method on a button inside
override func viewDidLayoutSubviews()
and it is introducing a lot of lag as well as adjusted the position/shrank the button. Also have attempted :@IBOutlet fileprivate weak var button: UIButton! { didSet { button.round(corners: [.topRight, .bottomLeft], radius: 50 , borderColor: UIColor.blue, borderWidth: 5.0) button.layoutSubviews() } }
the lag is now gone but the button is still shrunk/not constrained properly. Thanks! Thanks! -
satoukum about 7 yearsFor me, sometimes the image wouldn't show when I rounded the top corners, so I needed to add myView.layoutIfNeeded() to the line before.
-
Fattie about 7 yearshi @satoukum - the correct way to handle that in modern Xcode is shown in my answer below, cheers
-
user1673099 about 7 yearsnot working for bottom. i.e. bottomLeft & bottomRight
-
user1673099 about 7 yearsnot working for bottom. i.e. bottomLeft & bottomRight
-
Sharukh Mastan almost 7 yearsI had to set self.layer.masksToBounds = true in the view before invoking round() method to show the changes.
-
ricardopereira over 6 yearsBe careful with the
addBorder(mask:borderColor:borderWidth:)
method because it's always adding a new layer. IfviewDidLayoutSubviews
orlayoutSubviews
is called 5 times using theround(corners:radius:borderColor:borderWidth:)
method in an empty UIView... that view will have 5 sublayers! -
ricardopereira over 6 yearsHave in mind that the
lineWidth
doesn't check the screen scale, so theborderWidth
should be multiplied withUIScreen.main.scale
. -
Bibek over 6 yearsHow to use this when my view size is dynamic? lets say multiline label. When should I call the function to round?
-
iwasrobbed over 6 years@Dari See above: in the
viewDidLayoutSubviews
lifecycle callback -
Piotr Wasilewicz about 6 yearsnot working in Swift 4. Using [.bottomLeft, .bottomRight] I have rounded only bottomLeft corner.
-
Piotr Wasilewicz about 6 yearswhat If I want only three of four corners? [.bottomLeft, .bottomRight, .topRight] not working.
-
monolith almost 6 yearsbig caveat, this solution is only iOS 11+
-
Vadlapalli Masthan almost 5 yearshow can set border or shadow to this view ?
-
budiDino almost 5 years@VadlapalliMasthan the same way you usually would. Just make sure the frame of the view is set before rounding corners and applying shadow and border
-
Rey Bruno almost 5 yearsWork for me, topLeft and topRight corners, swift 4.0
-
Tiago Martins Peres over 4 yearsHi Mohammad Akbari, welcome. Please consider adding an explanation and format the code properly.
-
Amit Thakur about 4 yearsFor top two corners you can use view.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
-
brainray about 4 yearsGreat one thanks. While the naming like layerMinXMinYCorner is technically correct I wonder why Swift has to be that ugly at times
-
Pavan Kumar almost 4 years@iwasrobbed Only works for topLeft and bottomLeft. Not working for right side, i.e., topRight, bottomRight. Any suggestion.