Search code examples
ioscore-graphicscgcontext

Drawing Round corners For Custom Shape by Core Graphics


i am drawing Custom shape using Core Graphics and i want to make Rounded Corners for this shape this is my code of Drawing my custom Shape

CGPoint p1=[self getPointFromAngleQuarter:start_angle2 andRaduis:card.small_Raduis andCenter:center];
CGContextMoveToPoint(context, p1.x, p1.y);
CGPoint p2=[self getPointFromAngleQuarter:start_angle2 andCenter:center andRaduis:self.large_Raduis];
CGContextAddLineToPoint(context, p2.x, p2.y);
CGContextAddArc(context,center.x, center.y, selectedLargeRaduis, start, end,0);
CGPoint p5=[self getPointFromAngle:end_Angle andCenter:center andRaduis:self.small_Raduis];
CGContextAddLineToPoint(context, p5.x, p5.y);
CGContextAddArc(context,center.x, center.y,selectedSmallRaduis, end, start,1);
CGContextDrawPath(context, kCGPathFill);

and here is the final Result of my custom Shape

Custom Shape:

custom shape


Solution

  • If this shape is a solid color, the easy solution is to use a very wide line width, plus a round line cap and round line join. I presume, though, that you want this rounded shape to lay entirely inside the shape you included in your picture. Then the trick is to offset the arcs you draw by an amount equal to corner radius of the path (and stroke the line with twice the width of the corner radius).

    For example, considering this diagram (which is not the desired shape, but shows us how to get there):

    show how its done

    The black shape in the background is your original shape. The white path is the path I'm going to draw to achieve the rounded corners. The light gray is that path stroked with a large line width, a rounded line join, and a rounded line cap. The dark gray is that path filled in with another color.

    So hopefully this illustrates the idea. Create a new path, offset by the corner radius, and drawn with a line width twice the corner radius. If you simply draw the new path with a solid back stroke (replacing the light gray in the above image) and solid black fill (replacing the dark gray in the above image), you get your desired shape:

    final result

    Here is routine to get the path (the white line in my first image) in Objective-C:

    - (UIBezierPath *)arcWithRoundedCornerAt:(CGPoint)center
                                  startAngle:(CGFloat)startAngle
                                    endAngle:(CGFloat)endAngle
                                 innerRadius:(CGFloat)innerRadius
                                 outerRadius:(CGFloat)outerRadius
                                cornerRadius:(CGFloat)cornerRadius {
        CGFloat innerTheta = asin(cornerRadius / 2.0 / (innerRadius + cornerRadius)) * 2.0;
        CGFloat outerTheta = asin(cornerRadius / 2.0 / (outerRadius - cornerRadius)) * 2.0;
    
        UIBezierPath *path = [UIBezierPath bezierPath];
    
        [path addArcWithCenter:center
                        radius:innerRadius + cornerRadius
                    startAngle:endAngle - innerTheta
                      endAngle:startAngle + innerTheta
                     clockwise:false];
    
        [path addArcWithCenter:center
                        radius:outerRadius - cornerRadius
                    startAngle:startAngle + outerTheta
                      endAngle:endAngle - outerTheta
                     clockwise:true];
    
        [path closePath];
    
        return path;
    }
    

    Or in Swift 3:

    private func arcWithRoundedCorners(at center: CGPoint, startAngle: CGFloat, endAngle: CGFloat, innerRadius: CGFloat, outerRadius: CGFloat, cornerRadius: CGFloat) -> UIBezierPath {
        let innerTheta = asin(cornerRadius / 2 / (innerRadius + cornerRadius)) * 2
        let outerTheta = asin(cornerRadius / 2 / (outerRadius - cornerRadius)) * 2
    
        let path = UIBezierPath()
    
        path.addArc(withCenter: center, radius: innerRadius + cornerRadius, startAngle: endAngle - innerTheta, endAngle: startAngle + innerTheta, clockwise: false)
        path.addArc(withCenter: center, radius: outerRadius - cornerRadius, startAngle: startAngle + outerTheta, endAngle: endAngle - outerTheta, clockwise: true)
        path.close()
    
        return path
    }
    

    (You can do the above with Core Graphics calls if you want, but I generally use UIBezierPath.)

    If, though, you needed the fill to be a different color than the stroke, then the process is more complicated, because you can't just use this technique. Instead, you actually have to define a path that is an outline of the above shape, but consists of drawing not only the two big arcs, but four little arcs for each of the corners. It's tedious, but simple, trigonometry to construct that path, but I wouldn't go through that effort unless you had to.