Search code examples
iosobjective-cswiftuiviewuibezierpath

Making sunburst in Swift with a UIBezierPath


I want to make this UIView with UIBezierPath:

enter image description here

With the optional ability to spin it (although I can make this on my own if I know how to draw that sunburst (that is how I call the view))

Here the answer is given in Objective C: https://stackoverflow.com/a/14991292/7715250

I tried to convert it in Swift with this code (copy-paste in Playground will work):

import UIKit

class SunBurst: UIView{
    override func draw(_ rect: CGRect) {
        let radius: CGFloat = rect.size.width / 2.0
        UIColor.red.setFill()
        UIColor.blue.setStroke()
        let bezierPath = UIBezierPath()
        let centerPoint = CGPoint(x: rect.origin.x + radius, y: rect.origin.y + radius)
        var thisPoint = CGPoint(x: centerPoint.x + radius, y: centerPoint.y)
        bezierPath.move(to: centerPoint)
        var thisAngle: CGFloat = 0.0
        let sliceDegrees: CGFloat = 360.0 / 20 / 2.0
        let half: CGFloat = 180
        let floatRadius = Float(radius)

        for _ in 0..<20 {
            let x = CGFloat((floatRadius * cosf(Float(.pi * thisAngle + sliceDegrees / half)))) + centerPoint.x
            let y = CGFloat((floatRadius * sinf(Float(.pi * thisAngle + sliceDegrees / half)))) + centerPoint.y
            thisPoint = CGPoint(x: x, y: y)
            bezierPath.addLine(to: thisPoint)
            thisAngle += sliceDegrees
            let x2 = CGFloat((floatRadius * cosf(Float((.pi * thisAngle + sliceDegrees) / half)))) + centerPoint.x
            let y2 = CGFloat((floatRadius * sinf(Float((.pi * thisAngle + sliceDegrees) / half)))) + centerPoint.y
            thisPoint = CGPoint(x: x2, y: y2)
            bezierPath.addLine(to: thisPoint)
            bezierPath.addLine(to: centerPoint)
            thisAngle += sliceDegrees
        }
        bezierPath.close()
        bezierPath.lineWidth = 1
        bezierPath.fill()
        bezierPath.stroke()
    }
}


let custom = SunBurst(frame: CGRect(x: 0, y: 0, width: 500, height: 500))
custom.backgroundColor = .clear

With this result:

enter image description here

I think I am missing 1 line of code since it almost looks correct, but what what am I missing? I am surely not an expert with Objective C and I used https://objectivec2swift.com/#/home/converter/ to convert the code on the link with the answer.

Besides it almost looks correct, it should work in landscape mode. When I lower the height and retain the same width, the sunburst fails badly to draw. How can it also work in landscape?

Thanks for any help making the correct UIView. I think it would look nice for a bonus view in a game.


Solution

  • The problem is a lack of proper operator precedence in your degree to radian calculation.

    Replace each use (all four lines) of:

    .pi * thisAngle + sliceDegrees / half
    

    with:

    .pi * (thisAngle + sliceDegrees) / half
    

    Better, add a degreesToRadians function so you can simply pass thisAngle + sliceDegrees to it and avoid the problem all together.