Custom Segmented Control Effect - swift

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)
}
}

Related

Swift how to mask shape layer to blur layer

I was making a progress circle, and I want its track path to have a blur effect, is there any way to achieve that?
This is what the original track looks like(the track path is transparent, I want it to be blurred)
And this is my attempt
let outPutViewFrame = CGRect(x: 0, y: 0, width: 500, height: 500)
let circleRadius: CGFloat = 60
let circleViewCenter = CGPoint(x: outPutViewFrame.width / 2 , y: outPutViewFrame.height / 2)
let circleView = UIView()
let progressWidth: CGFloat = 8
circleView.frame.size = CGSize(width: (circleRadius + progressWidth) * 2, height: (circleRadius + progressWidth) * 2)
circleView.center = circleViewCenter
let circleTrackPath = UIBezierPath(arcCenter: CGPoint(x: circleView.frame.width / 2, y: circleView.frame.height / 2), radius: circleRadius, startAngle: -CGFloat.pi / 2, endAngle: 2 * CGFloat.pi, clockwise: true)
let blur = UIBlurEffect(style: .light)
let blurEffect = UIVisualEffectView(effect: blur)
blurEffect.frame = circleView.bounds
blurEffect.mask(withPath: circleTrackPath, inverse: false)
extension UIView {
func mask(withPath path: UIBezierPath, inverse: Bool = false) {
let maskLayer = CAShapeLayer()
if inverse {
path.append(UIBezierPath(rect: self.bounds))
maskLayer.fillRule = CAShapeLayerFillRule.evenOdd
}
maskLayer.path = path.cgPath
maskLayer.lineWidth = 5
maskLayer.lineCap = CAShapeLayerLineCap.round
self.layer.mask = maskLayer
}
}
Set maskLayer.fillRule to evenOdd, even when not inversed.
if inverse {
path.append(UIBezierPath(rect: self.bounds))
}
maskLayer.fillRule = CAShapeLayerFillRule.evenOdd
create the circleTrackPath by using a big circle and a smaller circle.
let circleCenter = CGPoint(x: circleView.frame.width / 2, y: circleView.frame.height / 2)
let circleTrackPath = UIBezierPath(ovalIn:
CGRect(origin: circleCenter, size: .zero)
.insetBy(dx: circleRadius, dy: circleRadius))
// smaller circle
circleTrackPath.append(CGRect(origin: circleCenter, size: .zero)
.insetBy(dx: circleRadius * 0.8, dy: circleRadius * 0.8))
Set circleTrackPath.usesEvenOddFillRule to true:
circleTrackPath.usesEvenOddFillRule = true
Now you have a blurred full circle. The non-blurred arc part can be implemented as another sublayer.
Here is a MCVE that you can paste into a playground:
let container = UIView(frame: CGRect(x: 0, y: 0, width: 200, height: 200))
// change this to a view of your choice
let image = UIImageView(image: UIImage(named: "my_image"))
let blur = UIVisualEffectView(effect: UIBlurEffect(style: .light))
container.addSubview(image)
blur.frame = image.frame
container.addSubview(blur)
let outer = image.bounds.insetBy(dx: 30, dy: 30)
let path = UIBezierPath(ovalIn: outer)
path.usesEvenOddFillRule = true
path.append(UIBezierPath(ovalIn: outer.insetBy(dx: 10, dy: 10)))
let maskLayer = CAShapeLayer()
maskLayer.path = path.cgPath
maskLayer.fillRule = .evenOdd
blur.layer.mask = maskLayer
container // <--- playground quick look this
Using my profile pic as the background, this produces:

How to add rounded corners to a UIView with gradient applied?

I've written custom subclass of UIView that draws a gradient inside of it:
import UIKit
#IBDesignable
class PlayerCellView: UIView {
var startColor: UIColor = UIColor(red:0.20, green:0.75, blue:1.00, alpha:1.00)
var endColor: UIColor = UIColor(red:0.07, green:0.42, blue:1.00, alpha:1.00)
override func draw(_ rect: CGRect) {
let context = UIGraphicsGetCurrentContext()
let colors = [startColor.cgColor, endColor.cgColor]
let locations : [CGFloat] = [0.0, 1.0]
let gradient = CGGradient(colorsSpace: CGColorSpaceCreateDeviceRGB(), colors: colors as CFArray, locations: locations)
context?.drawLinearGradient(gradient!, start: CGPoint.zero, end: CGPoint(x: 0, y: self.bounds.height), options: .drawsAfterEndLocation)
}
}
How would I now apply rounded edges to this view?
use this extension method:
extension UIView {
func addGradientLayer(with colors: [CGColor], startPoint: CGPoint, endPoint: CGPoint, locations: [NSNumber] = [0.0, 1.0], frame: CGRect = CGRect.zero) {
let gradientLayer = CAGradientLayer()
gradientLayer.colors = colors
gradientLayer.startPoint = startPoint
gradientLayer.endPoint = endPoint
gradientLayer.locations = locations
gradientLayer.frame = frame
gradientLayer.cornerRadius = self.layer.cornerRadius
self.layer.insertSublayer(gradientLayer, at: 0)
}
}
You can add a rounded corner UIView with gradient using the following method:
func makeCircularGradient(){
let circularView = UIView()
self.view.addSubview(circularView)
circularView.translatesAutoresizingMaskIntoConstraints = false
circularView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
circularView.centerYAnchor.constraint(equalTo: self.view.centerYAnchor).isActive = true
circularView.widthAnchor.constraint(equalToConstant: 200).isActive = true
circularView.heightAnchor.constraint(equalToConstant: 200).isActive = true
self.view.layoutIfNeeded()
let gradient = CAGradientLayer()
gradient.frame = circularView.bounds
gradient.colors = [UIColor.blue.cgColor,
UIColor.red.cgColor]
gradient.startPoint = CGPoint(x: 0, y: 1)
gradient.endPoint = CGPoint(x: 1, y: 0)
circularView.layer.addSublayer(gradient)
let circularPath = CGMutablePath()
circularPath.addArc(center: CGPoint.init(x: circularView.bounds.width / 2, y: circularView.bounds.height / 2), radius: circularView.bounds.width / 2, startAngle: 0, endAngle: CGFloat.pi * 2, clockwise: true, transform: .identity)
let maskLayer = CAShapeLayer()
maskLayer.path = circularPath
maskLayer.fillRule = kCAFillRuleEvenOdd
maskLayer.fillColor = UIColor.red.cgColor
circularView.layer.mask = maskLayer
}
Make a UIView and set the constraints as you see fit
Make a CAGradientLayer with colours of your choice
Make a circular mask layer and apply to the UIView.
The result will be something like below:
Making a curved corner radius View with gradient is a little bit more difficult than the circular one. And can be done like :
func makeCurvedCornerGradient(){
let circularView = UIView()
self.view.addSubview(circularView)
circularView.translatesAutoresizingMaskIntoConstraints = false
circularView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
circularView.centerYAnchor.constraint(equalTo: self.view.centerYAnchor).isActive = true
circularView.widthAnchor.constraint(equalToConstant: 200).isActive = true
circularView.heightAnchor.constraint(equalToConstant: 200).isActive = true
self.view.layoutIfNeeded()
let gradient = CAGradientLayer()
gradient.frame = circularView.bounds
gradient.colors = [UIColor.blue.cgColor,
UIColor.red.cgColor]
gradient.startPoint = CGPoint(x: 0, y: 1)
gradient.endPoint = CGPoint(x: 1, y: 0)
circularView.layer.addSublayer(gradient)
let circularPath = CGMutablePath()
circularPath.move(to: CGPoint.init(x: 20, y: 0))
circularPath.addLine(to: CGPoint.init(x: circularView.bounds.width - 20, y: 0))
circularPath.addQuadCurve(to: CGPoint.init(x: circularView.bounds.width, y: 20), control: CGPoint.init(x: circularView.bounds.width, y: 0))
circularPath.addLine(to: CGPoint.init(x: circularView.bounds.width, y: circularView.bounds.height - 20))
circularPath.addQuadCurve(to: CGPoint.init(x: circularView.bounds.width - 20, y: circularView.bounds.height), control: CGPoint.init(x: circularView.bounds.width, y: circularView.bounds.height))
circularPath.addLine(to: CGPoint.init(x: 20, y: circularView.bounds.height))
circularPath.addQuadCurve(to: CGPoint.init(x: 0, y: circularView.bounds.height - 20), control: CGPoint.init(x: 0, y: circularView.bounds.height))
circularPath.addLine(to: CGPoint.init(x: 0, y: 20))
circularPath.addQuadCurve(to: CGPoint.init(x: 20, y: 0), control: CGPoint.init(x: 0, y: 0))
let maskLayer = CAShapeLayer()
maskLayer.path = circularPath
maskLayer.fillRule = kCAFillRuleEvenOdd
maskLayer.fillColor = UIColor.red.cgColor
circularView.layer.mask = maskLayer
}
Change the values according to your need.
Output:
You can possibly use the Following Code:
func setGradientBorderWidthColor(view: UIView)
{
let gradientColor = CAGradientLayer()
gradientColor.frame = CGRect(origin: CGPoint.zero, size: view.frame.size)
gradientColor.colors = [UIColor.blue.cgColor, UIColor.green.cgColor]
let pathWithRadius = UIBezierPath(roundedRect:view.bounds, byRoundingCorners:[.topRight, .topLeft, .bottomLeft , .bottomRight], cornerRadii: CGSize(width: 20.0, height: 20.0))
let shape = CAShapeLayer()
shape.lineWidth = 3
shape.path = pathWithRadius.cgPath
shape.strokeColor = UIColor.black.cgColor
shape.fillColor = UIColor.clear.cgColor
gradientColor.mask = shape
view.layer.mask = shape
view.layer.addSublayer(gradientColor)
}
You can use this as:
let myView = UIView()
myView.backgroundColor = UIColor.gray
myView.frame = CGRect(x: 100, y: 100, width: 100, height: 100)
setGradientBorderWidthColor(view: myView)
self.view.addSubview(myView)
Output is like this:
Hope this Helps!

Animate different corners in CAShapeLayer

I am using the following code:
let MainLayer : CAShapeLayer = {
let mainlayer = CAShapeLayer()
mainlayer.fillColor = UIColor.white.cgColor
mainlayer.shadowColor = UIColor.darkGray.cgColor
mainlayer.shadowOffset = CGSize(width: 3.0, height: 3.0)
mainlayer.shadowOpacity = 0.5
mainlayer.shadowRadius = 7
return mainlayer
}()
var LayerRect:CGRect!
var bezierPath:UIBezierPath!
let CellHeightMargin:CGFloat = 6.0
let CellWidthMargin:CGFloat = 15.0
override func viewDidLoad() {
super.viewDidLoad()
LayerRect = CGRect(x: self.bounds.origin.x, y: self.bounds.origin.y, width: UIScreen.main.bounds.width - CellWidthMargin * 2, height: self.bounds.height - CellHeightMargin * 2)
bezierPath = UIBezierPath(roundedRect: LayerRect, byRoundingCorners: [.bottomRight,.topRight], cornerRadii: CGSize(width: 5, height: 5))
MainLayer.path = bezierPath.cgPath
self.mainView.layer.insertSublayer(MainLayer, at: 0)
}
As you can see, byRoundingCorners is to bottom right and top right corners, now I want to replace it by the bottom left and bottom right with animation. How to do that?
By the following:
let ToStatusLayerPath = UIBezierPath(roundedRect: CellView.statusView.bounds, byRoundingCorners: [.bottomRight,.bottomLeft], cornerRadii: CGSize(width: 5, height: 5))
let animation = CABasicAnimation(keyPath: "path")
animation.toValue = ToStatusLayerPath.cgPath
animation.duration = 1.0;
animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut)
animation.fillMode = kCAFillModeForwards
animation.isRemovedOnCompletion = false
statusLayer.add(animation, forKey: nil)

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
}
}
}

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

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 }
}
}