How to set layer cornerRadius for only bottom-left, bottom-right, and top-left corner? - swift

How to set corner radius only only bottom-left, bottom-right, and top-left corners of a textview?
let rectShape = CAShapeLayer()
rectShape.backgroundColor = UIColor.redColor().CGColor
rectShape.bounds = messages.frame
rectShape.position = messages.center
rectShape.path = UIBezierPath(roundedRect: messages.bounds, byRoundingCorners: .BottomLeft | .TopRight, cornerRadii: CGSize(width: 20, height: 20)).CGPath
messages.layer.addSublayer(rectShape)
this code creates two rects. I dont know why.

(swift 4/iOS 11) Just simply say for bottom:
yourView.clipsToBounds = true
yourView.layer.cornerRadius = 10
yourView.layer.maskedCorners = [.layerMaxXMaxYCorner, .layerMinXMaxYCorner]
for Up:
yourView.clipsToBounds = true
yourView.layer.cornerRadius = 10
yourView.layer.maskedCorners = [.layerMaxXMinYCorner, .layerMinXMinYCorner]
in your case:
yourView.layer.maskedCorners = [.layerMaxXMaxYCorner, .layerMinXMaxYCorner, .layerMinXMinYCorner]
Hope this help :)

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

Swift 3
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
}
}
Use like this
YourView.roundCorners([.topLeft, .bottomLeft], radius: 10)

A better answer for both iOS 11 and iOS 10 bottom corner would be
if #available(iOS 11.0, *){
view.clipsToBounds = true
view.layer.cornerRadius = 10
view.layer.maskedCorners = [.layerMaxXMaxYCorner, .layerMinXMaxYCorner]
}else{
let rectShape = CAShapeLayer()
rectShape.bounds = view.frame
rectShape.position = view.center
rectShape.path = UIBezierPath(roundedRect: view.bounds, byRoundingCorners: [.bottomLeft , .bottomRight], cornerRadii: CGSize(width: 20, height: 20)).cgPath
view.layer.backgroundColor = UIColor.green.cgColor
view.layer.mask = rectShape
}
in case this didnt work on iOS 10 and below, try running the code in viewDidLayoutSubviews() of your viewcontroller class like this
override func viewDidLayoutSubviews() {
if #available(iOS 11.0, *){
}else{
let rectShape = CAShapeLayer()
rectShape.bounds = view.frame
rectShape.position = view.center
rectShape.path = UIBezierPath(roundedRect: view.bounds, byRoundingCorners: [.bottomLeft , .bottomRight], cornerRadii: CGSize(width: 20, height: 20)).cgPath
view.layer.backgroundColor = UIColor.green.cgColor
view.layer.mask = rectShape
}

Swift 4+
func roundCorners(with CACornerMask: CACornerMask, radius: CGFloat) {
self.layer.cornerRadius = radius
self.layer.maskedCorners = [CACornerMask]
}
How to use
//Top right
roundCorners(with: [.layerMaxXMinYCorner], radius: 20)
//Top left
roundCorners(with: [.layerMinXMinYCorner], radius: 20)
//Bottom right
roundCorners(with: [.layerMaxXMaxYCorner], radius: 20)
//Bottom left
roundCorners(with: [.layerMinXMaxYCorner], radius: 20)
OR
Another way using CACornerMask
extension UIView{
enum RoundCornersAt{
case topRight
case topLeft
case bottomRight
case bottomLeft
}
//multiple corners using CACornerMask
func roundCorners(corners:[RoundCornersAt], radius: CGFloat) {
self.layer.cornerRadius = radius
self.layer.maskedCorners = [
corners.contains(.topRight) ? .layerMaxXMinYCorner:.init(),
corners.contains(.topLeft) ? .layerMinXMinYCorner:.init(),
corners.contains(.bottomRight) ? .layerMaxXMaxYCorner:.init(),
corners.contains(.bottomLeft) ? .layerMinXMaxYCorner:.init(),
]
}
}
You can use like below
myView.roundCorners(corners: [.topLeft,.bottomLeft], radius: 20)
OR
Multiple corners using UIRectCorner
func roundedCorners(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
layer.mask = mask
}
How to use
roundedCorners(corners: [.topLeft, .topRight], radius: 20)

For subview & POPUPs [Swift 5]
override func layoutSublayers(of layer: CALayer) {
searchBarPopup.clipsToBounds = true
searchBarPopup.layer.cornerRadius = 10
searchBarPopup.layer.maskedCorners = [ .layerMaxXMinYCorner, .layerMinXMinYCorner]
}
output:

Swift 4
override func viewDidLoad() {
let topRight = UIView(frame: CGRect(x: 120, y: 200, width: 120, height: 120))
topRight.roundedTop()
topRight.backgroundColor = .red
self.view.center = topRight.center
self.view.addSubview(topRight)
super.viewDidLoad()
}
Output :
extension on UIView Swift 4 : Reference Link

Here is an extension for iOS 11+
import Foundation
import UIKit
extension UIView {
func roundCorners(_ corners: CACornerMask, radius: CGFloat, borderColor: UIColor, borderWidth: CGFloat) {
self.layer.maskedCorners = corners
self.layer.cornerRadius = radius
self.layer.borderWidth = borderWidth
self.layer.borderColor = borderColor.cgColor
}
}
Usage:-
self.yourView.roundCorners([.layerMaxXMaxYCorner, .layerMaxXMinYCorner], radius: 20.0, borderColor: UIColor.green, borderWidth: 1)
Swift 5+
view.layer.maskedCorners = [.layerMaxXMaxYCorner, .layerMaxXMinYCorner]

Add RoundedCornerView.swift file to your project
Add an UIView to your ViewController
Change Custom Class in Identity Inspector to RoundedCornerView
Go to Attribute Inspector pick Corner Radius (CGFloat) and On the corners you want rounded.
RoundedCornerView.swift
import UIKit
#IBDesignable
class RoundedCornerView: UIView {
var cornerRadiusValue : CGFloat = 0
var corners : UIRectCorner = []
#IBInspectable public var cornerRadius : CGFloat {
get {
return cornerRadiusValue
}
set {
cornerRadiusValue = newValue
}
}
#IBInspectable public var topLeft : Bool {
get {
return corners.contains(.topLeft)
}
set {
setCorner(newValue: newValue, for: .topLeft)
}
}
#IBInspectable public var topRight : Bool {
get {
return corners.contains(.topRight)
}
set {
setCorner(newValue: newValue, for: .topRight)
}
}
#IBInspectable public var bottomLeft : Bool {
get {
return corners.contains(.bottomLeft)
}
set {
setCorner(newValue: newValue, for: .bottomLeft)
}
}
#IBInspectable public var bottomRight : Bool {
get {
return corners.contains(.bottomRight)
}
set {
setCorner(newValue: newValue, for: .bottomRight)
}
}
func setCorner(newValue: Bool, for corner: UIRectCorner) {
if newValue {
addRectCorner(corner: corner)
} else {
removeRectCorner(corner: corner)
}
}
func addRectCorner(corner: UIRectCorner) {
corners.insert(corner)
updateCorners()
}
func removeRectCorner(corner: UIRectCorner) {
if corners.contains(corner) {
corners.remove(corner)
updateCorners()
}
}
func updateCorners() {
let path = UIBezierPath(roundedRect: self.bounds, byRoundingCorners: corners, cornerRadii: CGSize(width: cornerRadiusValue, height: cornerRadiusValue))
let mask = CAShapeLayer()
mask.path = path.cgPath
self.layer.mask = mask
}
}
github link : RoundedCornerView

The best solution I could come up with all of the above solution is this.
extension UIView {
func roundCorners(_ corners: UIRectCorner, radius: CGFloat) {
if #available(iOS 11, *) {
var cornerMask = CACornerMask()
if(corners.contains(.topLeft)){
cornerMask.insert(.layerMinXMinYCorner)
}
if(corners.contains(.topRight)){
cornerMask.insert(.layerMaxXMinYCorner)
}
if(corners.contains(.bottomLeft)){
cornerMask.insert(.layerMinXMaxYCorner)
}
if(corners.contains(.bottomRight)){
cornerMask.insert(.layerMaxXMaxYCorner)
}
self.layer.cornerRadius = radius
self.layer.maskedCorners = cornerMask
} 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
}
}
}
I believe that this way the implementation is easy while selecting the sides.
view.roundCorners([.bottomLeft, .bottomRight, .topLeft], radius: 16)

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

issue solved right top and right bottom work now
Tested code in iOS 9 , 10 , 11 version
extension UIView {
func roundCorners(_ corners:UIRectCorner,_ cormerMask:CACornerMask, radius: CGFloat) {
if #available(iOS 11.0, *){
self.clipsToBounds = false
self.layer.cornerRadius = radius
self.layer.maskedCorners = cormerMask
}else{
let rectShape = CAShapeLayer()
rectShape.bounds = self.frame
rectShape.position = self.center
rectShape.path = UIBezierPath(roundedRect: self.bounds, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius)).cgPath
self.layer.mask = rectShape
}
}
}

As #kazi.munshimun gave a view-only setup suggestion, it is wonderful, however I didn't want create a whole view for this, and I was looking for a one-only field for cornerRadius.
So I came up with the idea of flag combinations where for example BottomLeft(4) + BottomRight(8) = 12
Therefore, it needs on your view to set up the number combination from 0 (none) to 15 (all of corners) + clipsToBounds enabled. And that's it ! Entirely customizable through file.
This solution example works for iOS 11.0+ (Swift 4+) It can be adapted for lower versions :
extension UIView {
enum CornerEnum: Int {
case None = 0
case TopLeft = 1
case TopRight = 2
case BottomLeft = 4
case BottomRight = 8
}
#IBInspectable
var radiusCorners: Int {
set {
var corners = CACornerMask()
if(newValue & CornerEnum.TopLeft.rawValue != 0){
corners.insert(.layerMinXMinYCorner)
}
if(newValue & CornerEnum.TopRight.rawValue != 0){
corners.insert(.layerMaxXMinYCorner)
}
if(newValue & CornerEnum.BottomLeft.rawValue != 0){
corners.insert(.layerMinXMaxYCorner)
}
if(newValue & CornerEnum.BottomRight.rawValue != 0){
corners.insert(.layerMaxXMaxYCorner)
}
layer.maskedCorners = corners
}
get { return self.radiusCorners }
}
}

Related

can't make round corners for UIView

I have a UIView object inside a horizontal stackView.
I'm calling this function inside LayoutSubviews:
func roundCorners(corners: UIRectCorner, radius: CurveRadius) {
var r = CGFloat()
switch radius {
case .small:
r = self.frame.width / 30
case .medium:
r = self.frame.width / 25
}
let path = UIBezierPath(roundedRect: bounds, byRoundingCorners: corners, cornerRadii: CGSize(width: r, height: r))
let mask = CAShapeLayer()
mask.path = path.cgPath
layer.mask = mask
}
but it just doesn't work. I guess it has something to do with a stackView. can anyone help me out?
extension UIView {
/// Rounds ``UIView`` corners.
/// - Parameters:
/// - maskedCorners: Corners to be rounded.
/// - cornerRadius: Value to be set as corner radius.
func roundCorners(maskedCorners: CACornerMask,
cornerRadius: CGFloat) {
layer.cornerRadius = cornerRadius
layer.maskedCorners = maskedCorners
layer.masksToBounds = true
}
}
I'm not sure if the UIStackView is the problem here, but there's another, more re-usable way to do this using a CACornerMask on the UIView's layer in an extension:
extension UIView {
func roundCorners() {
self.layer.cornerRadius = 10
self.layer.maskedCorners = [.layerMaxXMaxYCorner, .layerMaxXMinYCorner]
}
}
When you create your view, just call the roundCorners(), like this:
private lazy var roundCorneredView: UIView = {
let view = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
view.backgroundColor = .red
view.roundCorners()
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
Then add that view to your hierarchy in the usual way using addSubview.
CACornerMask docs # Apple

Custom UIButton Causes Storyboard Agent Crash Swift

I created a custom UIButton class and it causing the storyboard agent to fail.
I'm Including my extensions cause I really don't know what the problem is.
I tried to debug this view from the storyboard but it sends me straight to assembly code.
I tried to make it a without #IBDesignable, but it still cause a crash.
Also if you tips for improving how I'm writing my class I'll be glad to hear them.
I'll be glad if you can help me
This is my class:
#IBDesignable class customButton: UIButton{
private let imagesPadding: CGFloat = 2
private var ArrowSymbleImageView: UIImageView!
#IBInspectable var iconImageInspectable: UIImage = UIImage(systemName: "globe")!{
willSet {
if (ArrowSymbleImageView != nil) {
ArrowSymbleImageView.image = newValue
}
}
}
#IBInspectable var BackgroundColorInspectable: UIColor = .white {
willSet {
self.backgroundColor = newValue
if (ArrowSymbleImageView != nil) {
if (self.BackgroundColorInspectable.isDarkColor) {
ArrowSymbleImageView.tintColor = .white
}else{
ArrowSymbleImageView.tintColor = .black
}
}
}
}
required init?(coder: NSCoder) {
super.init(coder: coder)
self.generalInit()
}
private func generalDeinit() {
ArrowSymbleImageView.removeFromSuperview()
}
private func generalInit() {
self.backgroundColor = self.BackgroundColorInspectable
self.roundCorners(corners: [.bottomLeft], radius: self.width() / 2 * 0.7)
self.dropShadow()
let sizePartFromView: CGFloat = 4
ArrowSymbleImageView = UIImageView(frame: CGRect(x: self.width() / 2 - (self.width() / sizePartFromView / 2),
y: self.height() / 2 - (self.height() / sizePartFromView / 2),
width: self.width() / sizePartFromView,
height: self.height() / sizePartFromView))
ArrowSymbleImageView.image = self.iconImageInspectable
if (self.BackgroundColorInspectable.isDarkColor) {
ArrowSymbleImageView.tintColor = .white
}else{
ArrowSymbleImageView.tintColor = .black
}
ArrowSymbleImageView.contentMode = .scaleAspectFill
self.addSubview(ArrowSymbleImageView)
}
}
internal 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
}
}
internal extension UIView {
func dropShadow(scale: Bool = true, size: CGSize = CGSize(width: -2, height: 2)) {
layer.masksToBounds = false
layer.shadowColor = UIColor.black.cgColor
layer.shadowOpacity = 0.5
layer.shadowOffset = size
layer.shadowRadius = 1
layer.shadowPath = UIBezierPath(rect: bounds).cgPath
layer.shouldRasterize = true
layer.rasterizationScale = scale ? UIScreen.main.scale : 1
}
func dropShadow(color: UIColor, opacity: Float = 0.5, offSet: CGSize, radius: CGFloat = 1, scale: Bool = true) {
layer.masksToBounds = false
layer.shadowColor = color.cgColor
layer.shadowOpacity = opacity
layer.shadowOffset = offSet
layer.shadowRadius = radius
layer.shadowPath = UIBezierPath(rect: self.bounds).cgPath
layer.shouldRasterize = true
layer.rasterizationScale = scale ? UIScreen.main.scale : 1
}
}
internal extension UIColor
{
var isDarkColor: Bool {
var r, g, b, a: CGFloat
(r, g, b, a) = (0, 0, 0, 0)
self.getRed(&r, green: &g, blue: &b, alpha: &a)
let lum = 0.2126 * r + 0.7152 * g + 0.0722 * b
return lum < 0.50 ? true : false
}
}
I noticed this:
UIImage(systemName: "globe")!
Is there a reason why you instantiate a custom image this way? Is "globe" an apple provided default image?
You should be really using, if this isn't a default image.
UIImage(named:"globe")!
Dont use Force unwrapping when you do not have confirmation about data always use optional binding and do change following line in code from this UIImage(systemName: "globe")! to UIImage(named:"globe")! .

Swift make UIView border into Gradient Color

I want to create a gradient border on a UIView.
The following code can produce a solid for the view Box1.
nothing I have found allows me to change the frameColor to a gradient.
var frameColor = UIColor.red.cgColor
var leftBorder = CALayer()
leftBorder.frame = CGRect(x: 0.0, y: 0, width: 1, height: box1.frame.size.height )
leftBorder.backgroundColor = frameColor
I am using Swift 5 and Xcode 11.
Thank you.
Try with below extension
public extension UIView {
private static let kLayerNameGradientBorder = "GradientBorderLayer"
func setGradientBorder(
width: CGFloat,
colors: [UIColor],
startPoint: CGPoint = CGPoint(x: 0.5, y: 0),
endPoint: CGPoint = CGPoint(x: 0.5, y: 1)
) {
let existedBorder = gradientBorderLayer()
let border = existedBorder ?? CAGradientLayer()
border.frame = bounds
border.colors = colors.map { return $0.cgColor }
border.startPoint = startPoint
border.endPoint = endPoint
let mask = CAShapeLayer()
mask.path = UIBezierPath(roundedRect: bounds, cornerRadius: 0).cgPath
mask.fillColor = UIColor.clear.cgColor
mask.strokeColor = UIColor.white.cgColor
mask.lineWidth = width
border.mask = mask
let exists = existedBorder != nil
if !exists {
layer.addSublayer(border)
}
}
func removeGradientBorder() {
self.gradientBorderLayer()?.removeFromSuperlayer()
}
private func gradientBorderLayer() -> CAGradientLayer? {
let borderLayers = layer.sublayers?.filter { return $0.name == UIView.kLayerNameGradientBorder }
if borderLayers?.count ?? 0 > 1 {
fatalError()
}
return borderLayers?.first as? CAGradientLayer
}
}
Usage
self.borderView.setGradientBorder(width: 5.0, colors: [UIColor.red , UIColor.blue])
I hope this will reach your requirement.
https://medium.com/#kushal0409/gradient-button-3dc8ef763039
There is no off-the-shelf way to do this, but you could build it yourself.
Off the top of my head, the approach might look like this:
Create a CAGradientLayer and set it up to draw your gradient as
desired.
Create a CAShapeLayer that contains an empty rectangle that is your
frame.
Add your shape layer as the mask of your gradient layer.
Add the gradient layer to your view as a sub-layer of the view's
content layer.
Swift 5 extension (works with corner radius)
extension UIView {
func gradientBorder(colors: [UIColor], isVertical: Bool){
self.layer.masksToBounds = true
//Create gradient layer
let gradient = CAGradientLayer()
gradient.frame = CGRect(origin: CGPoint.zero, size: self.bounds.size)
gradient.colors = colors.map({ (color) -> CGColor in
color.cgColor
})
//Set gradient direction
if(isVertical){
gradient.startPoint = CGPoint(x: 0.5, y: 0)
gradient.endPoint = CGPoint(x: 0.5, y: 1)
}
else {
gradient.startPoint = CGPoint(x: 0, y: 0.5)
gradient.endPoint = CGPoint(x: 1, y: 0.5)
}
//Create shape layer
let shape = CAShapeLayer()
shape.lineWidth = 1
shape.path = UIBezierPath(roundedRect: gradient.frame.insetBy(dx: 1, dy: 1), cornerRadius: self.layer.cornerRadius).cgPath
shape.strokeColor = UIColor.black.cgColor
shape.fillColor = UIColor.clear.cgColor
gradient.mask = shape
//Add layer to view
self.layer.addSublayer(gradient)
gradient.zPosition = 0
}
}

Custom Segmented Control Effect

There is what I want to do :
However, with Segmented Control : The only I can do is to choose background and tintColor.
How can I personalize Segmented Control to achieve this goal?
If not possible, How could I do ?
Many Thanks!
Try this in Swift 3:
mySegment.isMomentary = true
mySegment.layer.cornerRadius = mySegment.bounds.height / 2
mySegment.layer.borderColor = UIColor.black.cgColor
mySegment.layer.borderWidth = 1
mySegment.tintColor = UIColor.black
mySegment.clipsToBounds = true
Or you must to use UIBezierPath and CAShapeLayer
var OldShape = CAShapeLayer()
var OldShape2 = CAShapeLayer()
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
mySegment.isMomentary = true
mySegment.tintColor = UIColor.red
mySegment.setTitle("Oui", forSegmentAt: 0)
mySegment.setTitle("Non", forSegmentAt: 1)
drawBorderLine(myView: mySegment)
mySegment.layer.cornerRadius = mySegment.bounds.height / 2
mySegment.clipsToBounds = true
}
func drawBorderLine(myView : UISegmentedControl) {
let path = UIBezierPath(roundedRect: myView.bounds,
byRoundingCorners: [.topLeft, .bottomLeft, .bottomRight, .topRight],
cornerRadii: CGSize(width: 20.0, height: myView.bounds.height / 2))
path.move(to: CGPoint(x: myView.bounds.midX, y: myView.bounds.minY))
path.addLine(to: CGPoint(x: myView.bounds.midX, y: myView.bounds.maxY))
let shape = CAShapeLayer()
shape.path = path.cgPath
shape.fillColor = UIColor.clear.cgColor
shape.lineWidth = 3
shape.lineCap = kCALineCapRound
shape.strokeColor = UIColor.lightGray.cgColor
myView.layer.insertSublayer(shape, at: 0)
}
#IBAction func valueChange(_ sender: UISegmentedControl) {
// setting shape red
let shape = CAShapeLayer()
shape.fillColor = UIColor.clear.cgColor
shape.lineWidth = 3
shape.lineCap = kCALineCapRound
shape.strokeColor = UIColor.red.cgColor
// setting shape lightGray
let shape2 = CAShapeLayer()
shape2.fillColor = UIColor.clear.cgColor
shape2.lineWidth = 3
shape2.lineCap = kCALineCapRound
shape2.strokeColor = UIColor.lightGray.cgColor
switch sender.selectedSegmentIndex {
case 1:
let element = sender.subviews[0]
let path = UIBezierPath(roundedRect: element.bounds, byRoundingCorners: [.topRight, .bottomRight],
cornerRadii: CGSize(width: 20.0, height: element.bounds.height / 2))
shape.path = path.cgPath
let element2 = sender.subviews[1]
let path2 = UIBezierPath(roundedRect: element2.bounds, byRoundingCorners: [.bottomLeft, .topLeft],
cornerRadii: CGSize(width: 20.0, height: element2.bounds.height / 2))
shape2.path = path2.cgPath
SelectAndDeselect(myViewSelect: element, myViewDeselect: element2, select: shape, deselect: shape2)
case 0:
let element = sender.subviews[1]
let path = UIBezierPath(roundedRect: element.bounds, byRoundingCorners: [.topLeft, .bottomLeft],
cornerRadii: CGSize(width: 20.0, height: element.bounds.height / 2))
shape.path = path.cgPath
let element2 = sender.subviews[0]
let path2 = UIBezierPath(roundedRect: element2.bounds, byRoundingCorners: [.bottomRight, .topRight],
cornerRadii: CGSize(width: 20.0, height: element2.bounds.height / 2))
shape2.path = path2.cgPath
SelectAndDeselect(myViewSelect: element, myViewDeselect: element2, select: shape, deselect: shape2)
default:
break
}
}
func SelectAndDeselect(myViewSelect : UIView, myViewDeselect : UIView, select : CAShapeLayer, deselect : CAShapeLayer ) {
if OldShape.path != nil {
OldShape.removeFromSuperlayer()
OldShape = select
myViewSelect.layer.addSublayer(OldShape)
}else{
OldShape = select
myViewSelect.layer.addSublayer(OldShape)
}
if OldShape2.path != nil{
OldShape2.removeFromSuperlayer()
OldShape2 = deselect
myViewDeselect.layer.addSublayer(OldShape2)
}else{
OldShape2 = deselect
myViewDeselect.layer.addSublayer(OldShape2)
}
}

Swift - UIView with shadow different corner radiuses [duplicate]

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;
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
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
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)
}
}
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)
}
}
Swift code example here: https://stackoverflow.com/a/35621736/308315
Not directly. You will have to:
Create a CAShapeLayer
Set its path to be a CGPathRef based on view.bounds but with only two rounded corners (probably by using +[UIBezierPath bezierPathWithRoundedRect:byRoundingCorners:cornerRadii:])
Set your view.layer.mask to be the CAShapeLayer
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;
}
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
}
}
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.
This would be the simplest answer:
yourView.layer.cornerRadius = 8
yourView.layer.masksToBounds = true
yourView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
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;
}
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.
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:
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;
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.
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
}
}
}
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)
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.
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
}
}
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.
// 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;
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>
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.
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])
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
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;
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))
}
}
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)
}
}
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)
}
}
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])
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
}
}
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
}
}
}