Silver and Golden Challenges Solutions

After checking the existing solutions at the forum share mine here as they are very different.

Post here only the code relevant to the solutions.

***** Silver Challenge Solution *****

struct Line {

// Sine of the angle as a CGFloat accepted by UIColor initializer
var angleSin: CGFloat {
    let x = Double(abs(Int32(end.x - begin.x)))
    let y = Double(abs(Int32(end.y - begin.y)))
    let h = sqrt(pow(x, 2) + pow(y, 2))
    return CGFloat(y / h)
}

}

class DrawView: UIView {

override func draw(_ rect: CGRect) {
    ...
    for (_, line) in currentLines {
        currentLineColor = UIColor(hue: line.angleSin, saturation: 1, brightness: 1, alpha: 1)
        ...
    }
}

}

***** Golden Challenge Solution *****

struct Circle {

var begin = CGPoint.zero
var end = CGPoint.zero

var ovalRectangle: CGRect { // Cicle is boring, oval is more facinating
    let x = CGFloat(Int32(end.x - begin.x))
    let y = CGFloat(Int32(end.y - begin.y))
    let size = CGSize(width: x, height: y)
    return CGRect(origin: begin, size: size)
}

var circleRectangle: CGRect {
    let x = CGFloat(abs(Int32(end.x - begin.x)))
    let y = CGFloat(abs(Int32(end.y - begin.y)))
    let side = min(x, y)
    let size = CGSize(width: side, height: side)
    let center = CGPoint(x: (begin.x + end.x) / 2, y: (begin.y + end.y) / 2)
    let origin = CGPoint(x: center.x - side / 2, y: center.y - side / 2)
    return CGRect(origin: origin, size: size)
}

//Using this assignment function prevents losing built-in memberwise initialiser plus adds defencing
mutating func setLocation(_ locations: [CGPoint]) {
    if locations.count != 2 {
        return
    }
    begin = locations[0]
    end = locations[1]
}

}

class DrawView: UIView {

...

var circleTouches = [NSValue: CGPoint]()
var currentCircle: Circle?
var finishedCircles = [Circle]()

...

private func strokeCircle(_ circle: Circle) {
    let path = UIBezierPath(ovalIn: circle.circleRectangle)
    path.lineWidth = lineThickness
    path.stroke()
}

override func draw(_ rect: CGRect) {
    finishedLineColor.setStroke()

    ...

    for circle in finishedCircles {
        strokeCircle(circle)
    }
    
    currentLineColor.setStroke()
    
    if let circle = currentCircle {
        strokeCircle(circle)
    }
    
    ...
}

//MARK: - Handling touches
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    if touches.count == 2 {
        currentCircle = Circle()
        updateCirle(fromTouches: touches)
    } else {
        ...
    }
    setNeedsDisplay()
}

override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
    if touches.count == 2, let _ = currentCircle {
            updateCirle(fromTouches: touches)
    } else {
        ...
    }
    setNeedsDisplay()
}

override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
    if touches.count == 2, let _ = currentCircle {
        updateCirle(fromTouches: touches)
        finishedCircles.append(currentCircle!)
        currentCircle = nil
        circleTouches.removeAll()
    } else {
        ...
    }
    setNeedsDisplay()
}

override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
    ...
    circleTouches.removeAll()
    currentCircle = nil
    setNeedsDisplay()
}

private func updateCirle(fromTouches touches: Set<UITouch>) {
    for touch in touches {
        let location = touch.location(in: self)
        let key = NSValue(nonretainedObject: touch)
        circleTouches[key] = location
    }
    let locations = Array(circleTouches.values)
    currentCircle!.setLocation(locations)
}

}