Search code examples
iosobjective-ccore-graphics

How to add round corner on irregular figure?


I want to round every corner of this. I use UIBezierPath to draw and set

path.lineCapStyle = kCGLineCapRound;
path.lineJoinStyle = kCGLineJoinRound;

that doesn't work.

Do I have a convenient way to do it,instead of a sequence of addLineToPoint and addArcWithCenter

enter image description here

In custom view

- (void)drawRect:(CGRect)rect {

    UIBezierPath *path = [UIBezierPath bezierPath];
    [path moveToPoint:CGPointMake(100, 100)];
    [path addLineToPoint:CGPointMake(150, 100)];
    [path addLineToPoint:CGPointMake(150, 150)];
    [path addLineToPoint:CGPointMake(200, 150)];
    [path addLineToPoint:CGPointMake(200, 200)];
    [path addLineToPoint:CGPointMake(250, 200)];
    [path addLineToPoint:CGPointMake(250, 250)];
    [path addLineToPoint:CGPointMake(100, 250)];
    [path closePath];
    path.lineCapStyle = kCGLineCapRound;
    path.lineJoinStyle = kCGLineJoinRound;

    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSaveGState(context);
    CGContextSetFillColorWithColor(context, UIColor.greenColor.CGColor);
    CGContextAddPath(context, path.CGPath);
    CGContextFillPath(context);
    CGContextRestoreGState(context);
}

In Viewcontroller

- (void)viewDidLoad {
    [super viewDidLoad];
    testvvv *test = [[testvvv alloc] initWithFrame:self.view.bounds];
    [self.view addSubview:test];
}

Solution

  • I guess you want this:

    rounded staircase

    There are Core Graphics functions for rounding corners that are not available on UIBezierPath. They are CGContextAddArcToPoint and CGPathAddArcToPoint. I'll explain how they work.

    First let's consider how you draw two lines meeting at a sharp corner. When the path is first initialized, its “current point” is at the origin:

    current point at origin

    You use CGPathMoveToPoint to move to the start of the first line at (x0, y0):

    after move

    Then you use CGPathAddLineToPoint to draw the first line, ending at the corner at (x1, y1):

    after first line

    Finally, you use CGPathAddLineToPoint again to draw the second line to (x2, y2):

    after second line

    Now let's use CGPathAddArcToPoint to round the corner instead. Rewind to just after you used CGPathMoveToPoint to move to the start of the first line:

    after move

    CGPathAddArcToPoint takes two points: the point at the corner (which won't be reached since it will round the corner) and the next point in the shape after the corner. So you pass it both (x1,y1) (the corner) and (x2,y2) (the end of the next line). You also pass it the radius of the corner:

    after first line and corner

    Notice that CGPathAddArcToPoint draws the first line for you, and draws arc that forms the rounded corner, and leaves the current point at the end of that arc. So you then use CGPathAddLineToPoint to draw the second line:

    after second line

    Okay, that explains (I hope) how CGPathAddArcToPoint works. CGContextAddArcToPoint works the same way, but operates on the context's path.

    To apply this to your staircase shape, you want to use an CG*AddArcToPoint function to draw every corner. Since you have a closed shape and you want no sharp corners, you don't need to use CG*AddLineToPoint at all. You can use the arc function to draw every line as well as the rounded corners.

    One thing to be careful of is where you start the shape. You can't start at a corner, because then you'd get a straight line out of the corner. Instead, it's easiest to start at the midpoint of one of the lines, far from any corner.

    Also, since you're doing this in drawRect:, you can just use the context's path instead of creating a separate UIBezierPath or CGPath object. Here's the code I used to draw the result above:

    - (void)drawRect:(CGRect)rect {
        CGPoint p[] = {
            {100, 100},
            {150, 100},
            {150, 150},
            {200, 150},
            {200, 200},
            {250, 200},
            {250, 250},
            {100, 250},
        };
        size_t count = (sizeof p) / (sizeof p[0]);
        CGPoint pLast = p[count - 1];
    
        CGContextRef gc = UIGraphicsGetCurrentContext();
    
        // Start at the midpoint of one of the lines.
        CGContextMoveToPoint(gc, 0.5 * (pLast.x + p[0].x), 0.5 * (pLast.y + p[0].y));
    
        for (size_t i = 1; i < count; ++i) {
            CGContextAddArcToPoint(gc, p[i-1].x, p[i-1].y, p[i].x, p[i].y, 10);
        }
        CGContextAddArcToPoint(gc, pLast.x, pLast.y, p[0].x, p[0].y, 10);
    
        // Connect the last corner's arc to the starting point.
        CGContextClosePath(gc);
    
        [UIColor.greenColor setFill];
        CGContextFillPath(gc);
    }