How to set layer cornerRadius for only bottom-left, bottom-right, and top-left corner?
You just need to mask the layer as shown below:
For Swift 3:
let rectShape = CAShapeLayer()
rectShape.bounds = self.myView.frame
rectShape.position = self.myView.center
rectShape.path = UIBezierPath(roundedRect: self.myView.bounds, byRoundingCorners: [.bottomLeft , .bottomRight , .topLeft], cornerRadii: CGSize(width: 20, height: 20)).cgPath
self.myView.layer.backgroundColor = UIColor.green.cgColor
//Here I'm masking the textView's layer with rectShape layer
self.myView.layer.mask = rectShape
Lower Version:
let rectShape = CAShapeLayer()
rectShape.bounds = self.myView.frame
rectShape.position = self.myView.center
rectShape.path = UIBezierPath(roundedRect: self.myView.bounds, byRoundingCorners: .BottomLeft | .BottomRight | .TopLeft, cornerRadii: CGSize(width: 20, height: 20)).CGPath
self.myView.layer.backgroundColor = UIColor.greenColor().CGColor
//Here I'm masking the textView's layer with rectShape layer
self.myView.layer.mask = rectShape
How to set cornerRadius for only top-left and top-right corner of a UIView?
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)
}
}
how to set cornerRadius for only bottom-left,bottom-right and top-left corner of a UIView?
You can do it like this:
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:self.viewOutlet.bounds byRoundingCorners:(UIRectCornerTopLeft | UIRectCornerBottomLeft | UIRectCornerBottomRight) cornerRadii:CGSizeMake(10.0, 10.0)];
CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
maskLayer.frame = self.view.bounds;
maskLayer.path = maskPath.CGPath;
self.viewOutlet.layer.mask = maskLayer;
Update:
If You need border just create another CAShapeLayer
and add it to view's layer as sublayer. Like this (place this code below upper code):
CAShapeLayer *borderLayer = [[CAShapeLayer alloc] init];
borderLayer.frame = self.view.bounds;
borderLayer.path = maskPath.CGPath;
borderLayer.lineWidth = 4.0f;
borderLayer.strokeColor = [UIColor blackColor].CGColor;
borderLayer.fillColor = [UIColor clearColor].CGColor;
[self.viewOutlet.layer addSublayer:borderLayer];
In swift 3.0 like this:
let maskPath = UIBezierPath.init(roundedRect: self.viewOutlet.bounds, byRoundingCorners:[.topLeft, .bottomLeft], cornerRadii: CGSize.init(width: 10.0, height: 10.0))
let maskLayer = CAShapeLayer()
maskLayer.frame = self.viewOutlet.bounds
maskLayer.path = maskPath.cgPath
self.viewOutlet.layer.mask = maskLayer
Corner radius only for top and bottom left corner of a view
A simple way to do this is to set the corner radius of your cell's content view, and then to prevent the right side corners of the contents from being rounded you could constrain them to have x trailing space to the content view (where x is your corner radius). This does require you to adjust your layout to account for the extra padding on the right side of your cells though.
Issue with left bottom and right bottom cornerradius to Uiview inside UICollectioviewCell
You may want to try the following code to handle diff iOS version
- Add the extension for UIView, you can modify it as your need.
extension UIView {
func roundCorners(corners: UIRectCorner, radius: CGFloat) {
if #available(iOS 11.0, *) {
let cornerMasks = [
corners.contains(.topLeft) ? CACornerMask.layerMinXMinYCorner : nil,
corners.contains(.topRight) ? CACornerMask.layerMaxXMinYCorner : nil,
corners.contains(.bottomLeft) ? CACornerMask.layerMinXMaxYCorner : nil,
corners.contains(.bottomRight) ? CACornerMask.layerMaxXMaxYCorner : nil,
corners.contains(.allCorners) ? [CACornerMask.layerMinXMinYCorner, CACornerMask.layerMaxXMinYCorner, CACornerMask.layerMinXMaxYCorner, CACornerMask.layerMaxXMaxYCorner] : nil
].compactMap({ $0 })
var maskedCorners: CACornerMask = []
cornerMasks.forEach { (mask) in maskedCorners.insert(mask) }
self.clipsToBounds = true
self.layer.cornerRadius = radius
self.layer.maskedCorners = maskedCorners
} else {
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
}
}
}
- use the function like that:
cell.productnamevw.roundCorners(corners: [.bottomLeft, .bottomRight], radius: 10)
One more thing, for your case, I would recommend you setup the cell in the cell itself
class FeaturedCell: UICollectionViewCell {
@IBOutlet private weak var productnamevw: UIView!
override func awakeFromNib() {
super.awakeFromNib()
self.productnamevw.roundCorners(corners: [.bottomLeft, .bottomRight], radius: 10)
}
override func layoutSubviews() {
super.layoutSubviews()
self.productnamevw.roundCorners(corners: [.bottomLeft, .bottomRight], radius: 10)
}
}
Round Specific Corners SwiftUI
There are two options, you can use a View
with a Path
, or you can create a custom Shape
. In both cases you can use them standalone, or in a .background(RoundedCorders(...))
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))
}
}
struct RoundedCorners: View {
var color: Color = .blue
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 - br))
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)
path.closeSubpath()
}
.fill(self.color)
}
}
}
Option 2: Custom Shape
struct ContentView : View {
var body: some View {
Text("Hello World!")
.foregroundColor(.white)
.font(.largeTitle)
.padding(20)
.background(RoundedCorners(tl: 0, tr: 30, bl: 30, br: 0).fill(Color.blue))
}
}
struct RoundedCorners: Shape {
var tl: CGFloat = 0.0
var tr: CGFloat = 0.0
var bl: CGFloat = 0.0
var br: CGFloat = 0.0
func path(in rect: CGRect) -> Path {
var path = Path()
let w = rect.size.width
let h = rect.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 - br))
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)
path.closeSubpath()
return path
}
}
Applying corner radius to a specific UIView corner inside Storyboard does not work for all corners
You should update the mask in the override of layoutSubviews
. As the constraints update the frame
of the view, layoutSubviews
is called, so that’s the right place to update the mask.
As an aside, I’d discourage the use of awakeFromNib
to configure the view. It works fine if you use storyboard, but programmatically created views won’t call this. If you do want to configure the view when it’s created, it’s better to put the code in some private configuration method which is called by init(frame:)
and init(coder:)
. That way it works for both storyboards and programmatically created views.
I might also suggest observers on the inspectable properties, to trigger the update of the corner rounding. You want the corner rounding to update automatically if you change these properties.
Thus:
@IBDesignable
public class RoundedView: UIView {
@IBInspectable public var topLeft: Bool = false { didSet { setNeedsLayout() } }
@IBInspectable public var topRight: Bool = false { didSet { setNeedsLayout() } }
@IBInspectable public var bottomLeft: Bool = false { didSet { setNeedsLayout() } }
@IBInspectable public var bottomRight: Bool = false { didSet { setNeedsLayout() } }
@IBInspectable public var cornerRadius: CGFloat = 0 { didSet { setNeedsLayout() } }
public override func layoutSubviews() {
super.layoutSubviews()
var options = UIRectCorner()
if topLeft { options.formUnion(.topLeft) }
if topRight { options.formUnion(.topRight) }
if bottomLeft { options.formUnion(.bottomLeft) }
if bottomRight { options.formUnion(.bottomRight) }
let path = UIBezierPath(roundedRect: bounds,
byRoundingCorners: options,
cornerRadii: CGSize(width: cornerRadius, height: cornerRadius))
let maskLayer = CAShapeLayer()
maskLayer.path = path.cgPath
layer.mask = maskLayer
}
}
Or, alternatively, if targeting iOS 11 and later, we can let CoreAnimation do the rounding:
@IBDesignable
public class RoundedView: UIView {
@IBInspectable public var topLeft: Bool = false { didSet { updateCorners() } }
@IBInspectable public var topRight: Bool = false { didSet { updateCorners() } }
@IBInspectable public var bottomLeft: Bool = false { didSet { updateCorners() } }
@IBInspectable public var bottomRight: Bool = false { didSet { updateCorners() } }
@IBInspectable public var cornerRadius: CGFloat = 0 { didSet { updateCorners() } }
public override init(frame: CGRect = .zero) {
super.init(frame: frame)
updateCorners()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
updateCorners()
}
}
private extension RoundedView {
func updateCorners() {
var corners = CACornerMask()
if topLeft { corners.formUnion(.layerMinXMinYCorner) }
if topRight { corners.formUnion(.layerMaxXMinYCorner) }
if bottomLeft { corners.formUnion(.layerMinXMaxYCorner) }
if bottomRight { corners.formUnion(.layerMaxXMaxYCorner) }
layer.maskedCorners = corners
layer.cornerRadius = cornerRadius
}
}
The nice thing about this latter approach is that CoreAnimation will honor our corner rounding if we happen to be animating the frame
of the view:
If you use the aforementioned layoutSubviews
approach, then you have to manage the animation manually (e.g. with CADisplayLink
).
How to add corner radius for UIView
You shouldn't use a mask for this, you can simply use the layer.maskedCorners
property.
layer.cornerRadius = r
layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
Related Topics
What's the State of Developing iOS Apps in Linux
iOS Swift: Could Not Cast Value Type '_Nscfnumber' to 'Nsstring'
Remove Tab Bar Item Text, Show Only Image
iOS - Corelocation and Geofencing While App Is Closed
How to Integrate Cocoapods with a Swift Project
Weak or Strong for Iboutlet and Other
What's the Point of Nsassert, Actually
Proper Usage of the Alamofire's Urlrequestconvertible
Resize Superview After Subviews Change Dynamically Using Autolayout
How to Install an Unsigned IPA File on My Device Using an Apple Developer Account
Audiokit: Using the New Aksequencer with Any Variety of the Callback Instruments
What Values Should I Use for Cfbundleversion and Cfbundleshortversionstring
How to Show the Loading Indicator in the Top Status Bar
Open Installed Pwa from External Url
Uicellview Cell Layout in Swift
Xcode 6: Can't Connect Any Iboutlet to Viewcontroller
Memory Leaks in the Swift Playground/Deinit{} Not Called Consistently