Search code examples
iosswiftuibezierpathcashapelayer

iOS Swift: How to merge two shapes into a solid color shape using UIBezierPath?


enter image description here

Hey~~ I am trying to draw a solid round dot with a rectangle handle on top of it. (backcolor: purple, forecolor: black)

However, the shapes are all correct, but when I am coloring it, there is a gap at the shape overlapping area, which cannot be filled with solid color.

How should I correct it? Thanks!

var shapeLayer: CAShapeLayer!

func draw() {
    let path = UIBezierPath()

    // draw a rectangle
    path.move(to: CGPoint(x: 90, y: 0))
    path.addLine(to: CGPoint(x: 90, y: 100))
    path.addLine(to: CGPoint(x: 110, y: 100))
    path.addLine(to: CGPoint(x: 110, y: 0))
    path.addLine(to: CGPoint(x: 90, y: 0))

    // draw a circle
    path.move(to: CGPoint(x: 100, y: 100))
    path.addArc(withCenter: CGPoint(x: 100, y: 100), radius: 50, startAngle: 0, endAngle: 2 * CGFloat.pi, clockwise: true)

    // fill colors with purple and black
    self.shapeLayer.path = path.cgPath
    self.shapeLayer?.fillColor = foreColor.cgColor
    self.backgroundColor = backColor
}

Solution

  • Draw your rectangle in the opposite direction:

    // draw a rectangle
    path.move(to: CGPoint(x: 90, y: 0))
    path.addLine(to: CGPoint(x: 110, y: 0))
    path.addLine(to: CGPoint(x: 110, y: 100))
    path.addLine(to: CGPoint(x: 90, y: 100))
    path.addLine(to: CGPoint(x: 90, y: 0))
    

    The non-zero winding fill used by CoreGraphics uses the direction of the lines to determine inside/outside.


    Alternatively, you can reverse the direction of your circle:

    path.addArc(withCenter: CGPoint(x: 100, y: 100), radius: 50,
        startAngle: 2 * CGFloat.pi, endAngle: 0, clockwise: false)