Search code examples
iosuibezierpath

Draw transparent Arc


I want to draw a transparent arc connecting two corners of the rect so that I can see what is behind the view through the arc.

I can able to draw the arc using the following code

override func draw(_ rect: CGRect) {
    // Drawing code
    let arcHeight: CGFloat = 10
    let arcColor = UIColor.blue
    let arcRect = CGRect(x: 0, y: rect.height - arcHeight, width: rect.width, height: arcHeight)
    let arcRadius = (arcHeight / 2) + (pow(arcRect.width, 2) / (8 * arcHeight))
    let arcCenter = CGPoint(x: arcRect.origin.x + (arcRect.width / 2), y: arcRect.origin.y + arcRadius)
    
    let angle = acos(arcRect.width / (2 * arcRadius))
    let startAngle = radians(180) + angle
    let endAngle = radians(360) - angle
    
    let path = UIBezierPath(arcCenter: arcCenter, radius: arcRadius / 2, startAngle: startAngle, endAngle: endAngle, clockwise: true)
    path.lineWidth = arcRadius
    arcColor.setStroke()
    path.stroke()
}

private func radians(_ degrees: CGFloat) -> CGFloat {
    return degrees * CGFloat(M_PI) / 180
}

So to make the arc transparent I need to fill the remaining part of the rect excluding the arc for that I tried the below code,

let fullPath = UIBezierPath(rect: bounds)
fullPath.append(path)
fullPath.usesEvenOddFillRule = true
fullPath.addClip()
arcColor.setFill()
fullPath.fill()

But I cannot achieve what I expect. Kindly guide me how to fill the rect with color excluding the arc.

Please find the screenshot below,

I want the white arc to be transparent

I want the white arc in the above image to be transparent, so that I can see what is behind the view through arc.


Solution

  • It appears you want something like this:

    enter image description here

    To obtain that, I used almost identically your code, but I first filled the whole rect with blue, and then I drew that the arc, using your code, but when we come to stroke the arc we stroke it with a blend mode of .clear, thus erasing the area under the arc.

    class MyView : UIView {
        override init(frame:CGRect) {
            super.init(frame:frame)
            self.isOpaque = false
        }
    
        required init?(coder aDecoder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
    
        override func draw(_ rect: CGRect) {
            // Drawing code
            let arcHeight: CGFloat = 10
            let arcColor = UIColor.blue
            arcColor.setFill() // <--
            let con = UIGraphicsGetCurrentContext() // <--
            con?.fill(rect) // <--
            let arcRect = CGRect(x: 0, y: rect.height - arcHeight, width: rect.width, height: arcHeight)
            // ... same as your code ...
            path.stroke(with: .clear, alpha: 1)
        }
    
        private func radians(_ degrees: CGFloat) -> CGFloat {
            return degrees * CGFloat(M_PI) / 180
        }
    
    }