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)
}
}