IOS: Is possible to rounder radius with different value in each corner
Asked Answered
I

3

8

I want to rounder my UIView with the value like that
top-left-radius:20; bottom-right-radius:5; bottom-left-radius:5; and top-right-radius:10;

   //For rounder `UIRectCornerBottomLeft & UIRectCornerBottomRight` I use

    UIBezierPath *maskPath0 = [UIBezierPath bezierPathWithRoundedRect:self.messageView.bounds byRoundingCorners:(UIRectCornerBottomLeft | UIRectCornerBottomRight) cornerRadii:CGSizeMake(5.0, 5.0)];

    CAShapeLayer *maskLayer0 = [[CAShapeLayer alloc] init];
    maskLayer0.frame = self.bounds;
    maskLayer0.path  = maskPath0.CGPath;
    self.messageView.layer.mask = maskLayer0;


    //For rounder `TopRight` I use

    UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:self.messageView.bounds byRoundingCorners:(UIRectCornerTopRight) cornerRadii:CGSizeMake(10.0, 10.0)];

    CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
    maskLayer.frame = self.bounds;
    maskLayer.path  = maskPath.CGPath;
    self.messageView.layer.mask = maskLayer;


    //For rounder `TopLeft` I use

    UIBezierPath *maskPath2 = [UIBezierPath bezierPathWithRoundedRect:self.messageView.bounds byRoundingCorners:(UIRectCornerTopLeft) cornerRadii:CGSizeMake(20.0, 20.0)];

    CAShapeLayer *maskLayer2 = [[CAShapeLayer alloc] init];
    maskLayer2.frame = self.bounds;
    maskLayer2.path  = maskPath2.CGPath;
    self.messageView.layer.mask = maskLayer2;

But the result I get is the View with corner radius TopLeft with value 20. How can I achieve this rounder? Any help would be much appreciated.

Infundibulum answered 5/4, 2016 at 10:22 Comment(4)
You need to add all the corner radius using same object, as of now your last object is applying the corner radius.Suntan
@VatsalK you are right, but now I don't know to add it in same object, can you describes it more in code. thank you so muchInfundibulum
In that case you need to draw the shape as per your requirements, you can refer this link OR you can make image of the same shape and set the image as background.Suntan
On this older question, it's worth noting that the latest answer - I sent it a bounty - is perfect, just paste it in and do nothing else.Garrik
H
10

UIBezierPath has no such method. But you can use bunch of addLineToPoint and addArcWithCenter methods to do it:

let minx = CGRectGetMinX(rect)
let miny = CGRectGetMinY(rect)
let maxx = CGRectGetMaxX(rect)
let maxy = CGRectGetMaxY(rect)

let path = UIBezierPath()
path.moveToPoint(CGPointMake(minx + topLeftRadius, miny))
path.addLineToPoint(CGPointMake(maxx - topRightRadius, miny))
path.addArcWithCenter(CGPointMake(maxx - topRightRadius, miny + topRightRadius), radius: topRightRadius, startAngle:3 * M_PI_2, endAngle: 0, clockwise: true)
path.addLineToPoint(CGPointMake(maxx, maxy - bottomRightRadius))
path.addArcWithCenter(CGPointMake(maxx - bottomRightRadius, maxy - bottomRightRadius), radius: bottomRightRadius, startAngle: 0, endAngle: M_PI_2, clockwise: true)
path.addLineToPoint(CGPointMake(minx + bottomLeftRadius, maxy))
path.addArcWithCenter(CGPointMake(minx + bottomLeftRadius, maxy - bottomLeftRadius), radius: bottomLeftRadius, startAngle: M_PI_2, endAngle: M_PI, clockwise: true)
path.addLineToPoint(CGPointMake(minx, miny + topLeftRadius))
path.addArcWithCenter(CGPointMake(minx + topLeftRadius, miny + topLeftRadius), radius: topLeftRadius, startAngle: M_PI, endAngle: 3 * M_PI_2, clockwise: true)
path.closePath()

For Obj-C:

CGFloat topLeftRadius = 20;
CGFloat topRightRadius = 10;
CGFloat bottomRightRadius = 5;
CGFloat bottomLeftRadius = 5;

CGFloat minx = CGRectGetMinX(self.messageView.bounds);
CGFloat miny = CGRectGetMinY(self.messageView.bounds);
CGFloat maxx = CGRectGetMaxX(self.messageView.bounds);
CGFloat maxy = CGRectGetMaxY(self.messageView.bounds);

UIBezierPath *path = [[UIBezierPath alloc] init];
[path moveToPoint:CGPointMake(minx + topLeftRadius, miny)];
[path addLineToPoint:CGPointMake(maxx - topRightRadius, miny)];
[path addArcWithCenter:CGPointMake(maxx - topRightRadius, miny + topRightRadius) radius: topRightRadius startAngle: 3 * M_PI_2 endAngle: 0 clockwise: YES];
[path addLineToPoint:CGPointMake(maxx, maxy - bottomRightRadius)];
[path addArcWithCenter:CGPointMake(maxx - bottomRightRadius, maxy - bottomRightRadius) radius: bottomRightRadius startAngle: 0 endAngle: M_PI_2 clockwise: YES];
[path addLineToPoint:CGPointMake(minx + bottomLeftRadius, maxy)];
[path addArcWithCenter:CGPointMake(minx + bottomLeftRadius, maxy - bottomLeftRadius) radius: bottomLeftRadius startAngle: M_PI_2 endAngle:M_PI clockwise: YES];
[path addLineToPoint:CGPointMake(minx, miny + topLeftRadius)];
[path addArcWithCenter:CGPointMake(minx + topLeftRadius, miny + topLeftRadius) radius: topLeftRadius startAngle: M_PI endAngle:3 * M_PI_2 clockwise: YES];
[path closePath];

CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
maskLayer.path = path.CGPath;
self.messageView.layer.mask = maskLayer;
Hamel answered 5/4, 2016 at 10:32 Comment(6)
it is swift right?, i am new in iOS, i don't know swift. if you don't mind, can you add a objective-c version. thank you so much ;)Infundibulum
Yes, it is Swift. I've translated it into Obj-C. Hope I've not missed any ';' or '[]'Hamel
Thank you so much. but I have to go home now, I will check it in tomorrow. then I will notify you ;)Infundibulum
I tried your solution in objective-c but unfortunatelly, the view is gone :((Infundibulum
I've checked it and got successful results. What do you use as a rect? You need to use self.messageView.bounds. Please, check updated Obj-C code.Hamel
sorry. i'm using self.messageView.frame so it not working ^^Infundibulum
F
8

Here's a UIBezierPath category that does what you want, following the pattern of some of the existing initializers:

extension UIBezierPath {
    public convenience init(roundedRect rect: CGRect, topLeftRadius: CGFloat?, topRightRadius: CGFloat?, bottomLeftRadius: CGFloat?, bottomRightRadius: CGFloat?) {
        self.init()

        assert(((bottomLeftRadius ?? 0) + (bottomRightRadius ?? 0)) <= rect.size.width)
        assert(((topLeftRadius ?? 0) + (topRightRadius ?? 0)) <= rect.size.width)
        assert(((topLeftRadius ?? 0) + (bottomLeftRadius ?? 0)) <= rect.size.height)
        assert(((topRightRadius ?? 0) + (bottomRightRadius ?? 0)) <= rect.size.height)

        // corner centers
        let tl = CGPoint(x: rect.minX + (topLeftRadius ?? 0), y: rect.minY + (topLeftRadius ?? 0))
        let tr = CGPoint(x: rect.maxX - (topRightRadius ?? 0), y: rect.minY + (topRightRadius ?? 0))
        let bl = CGPoint(x: rect.minX + (bottomLeftRadius ?? 0), y: rect.maxY - (bottomLeftRadius ?? 0))
        let br = CGPoint(x: rect.maxX - (bottomRightRadius ?? 0), y: rect.maxY - (bottomRightRadius ?? 0))

        //let topMidpoint = CGPoint(rect.midX, rect.minY)
        let topMidpoint = CGPoint(x: rect.midX, y: rect.minY)

        makeClockwiseShape: do {
            self.move(to: topMidpoint)

            if let topRightRadius = topRightRadius {
                self.addLine(to: CGPoint(x: rect.maxX - topRightRadius, y: rect.minY))
                self.addArc(withCenter: tr, radius: topRightRadius, startAngle: -CGFloat.pi/2, endAngle: 0, clockwise: true)
            }
            else {
                self.addLine(to: tr)
            }

            if let bottomRightRadius = bottomRightRadius {
                self.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY - bottomRightRadius))
                self.addArc(withCenter: br, radius: bottomRightRadius, startAngle: 0, endAngle: CGFloat.pi/2, clockwise: true)
            }
            else {
                self.addLine(to: br)
            }

            if let bottomLeftRadius = bottomLeftRadius {
                self.addLine(to: CGPoint(x: rect.minX + bottomLeftRadius, y: rect.maxY))
                self.addArc(withCenter: bl, radius: bottomLeftRadius, startAngle: CGFloat.pi/2, endAngle: CGFloat.pi, clockwise: true)
            }
            else {
                self.addLine(to: bl)
            }

            if let topLeftRadius = topLeftRadius {
                self.addLine(to: CGPoint(x: rect.minX, y: rect.minY + topLeftRadius))
                self.addArc(withCenter: tl, radius: topLeftRadius, startAngle: CGFloat.pi, endAngle: -CGFloat.pi/2, clockwise: true)
            }
            else {
                self.addLine(to: tl)
            }

            self.close()
        }
    }
}
Flack answered 27/4, 2017 at 7:39 Comment(1)
sent you a bounty! :) BTW I fixed a trivial syntax error.Garrik
T
4

Swift 5 UIView extension version of @Mikhail-Grebionkin's answer:

extension UIView {

    func makeCustomRound(topLeft: CGFloat = 0, topRight: CGFloat = 0, bottomLeft: CGFloat = 0, bottomRight: CGFloat = 0) {
        let minX = bounds.minX
        let minY = bounds.minY
        let maxX = bounds.maxX
        let maxY = bounds.maxY

        let path = UIBezierPath()
        path.move(to: CGPoint(x: minX + topLeft, y: minY))
        path.addLine(to: CGPoint(x: maxX - topRight, y: minY))
        path.addArc(withCenter: CGPoint(x: maxX - topRight, y: minY + topRight), radius: topRight, startAngle:CGFloat(3 * Double.pi / 2), endAngle: 0, clockwise: true)
        path.addLine(to: CGPoint(x: maxX, y: maxY - bottomRight))
        path.addArc(withCenter: CGPoint(x: maxX - bottomRight, y: maxY - bottomRight), radius: bottomRight, startAngle: 0, endAngle: CGFloat(Double.pi / 2), clockwise: true)
        path.addLine(to: CGPoint(x: minX + bottomLeft, y: maxY))
        path.addArc(withCenter: CGPoint(x: minX + bottomLeft, y: maxY - bottomLeft), radius: bottomLeft, startAngle: CGFloat(Double.pi / 2), endAngle: CGFloat(Double.pi), clockwise: true)
        path.addLine(to: CGPoint(x: minX, y: minY + topLeft))
        path.addArc(withCenter: CGPoint(x: minX + topLeft, y: minY + topLeft), radius: topLeft, startAngle: CGFloat(Double.pi), endAngle: CGFloat(3 * Double.pi / 2), clockwise: true)
        path.close()

        let mask = CAShapeLayer()
        mask.path = path.cgPath
        layer.mask = mask
    }
}

Tamartamara answered 13/4, 2020 at 6:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.