I am a Java programmer who recently started learning some Swift. I made an app that displays the Sierpinski triangle and adds triangles if the user pans up, and zooms in and out using the pinch gesture. I am wondering what I can do to make my code more Swift-y and how I can improve the zooming.
SierpinskiViewController
import UIKit
class SierpinskiViewController: UIViewController, SierpinskiViewDataSource {
private struct Constants {
static let defaultNumTriangles = 0
static let numDivideConstant = 48
}
@IBOutlet weak var sierpinskiView: SierpinskiView! {
didSet {
sierpinskiView.dataSource = self
sierpinskiView.addGestureRecognizer(UIPinchGestureRecognizer(target: sierpinskiView, action: "scale:"))
}
}
@IBAction func setNumTriangles(gesture: UIPanGestureRecognizer) {
switch gesture.state {
case .Ended: fallthrough
case .Changed:
let translation = gesture.translationInView(sierpinskiView)
let numTrianglesChange = -Int(translation.y) / Constants.numDivideConstant
if numTrianglesChange != 0 {
numTriangles += numTrianglesChange
gesture.setTranslation(CGPointZero, inView: sierpinskiView)
}
default: break
}
}
private var numTriangles = Constants.defaultNumTriangles {
didSet {
numTriangles = min(max(numTriangles, 0), 10)
updateUI()
}
}
private func updateUI() {
sierpinskiView.setNeedsDisplay()
}
func getNumTriangles(sender: SierpinskiView) -> Int? {
return numTriangles
}
}
SierpinskiView
import UIKit
protocol SierpinskiViewDataSource: class {
func getNumTriangles(sender: SierpinskiView) -> Int?
}
@IBDesignable
class SierpinskiView: UIView {
var triangleCenter : CGPoint {
return convertPoint(center, fromView: superview)
}
var scale : CGFloat = 0.95 {
didSet { setNeedsDisplay() }
}
var triangleTipLocation : CGFloat = 100 {
didSet {
if triangleTipLocation<0 {
triangleTipLocation = 0
}
setNeedsDisplay()
}
}
override func drawRect(rect: CGRect) {
draw()
}
weak var dataSource : SierpinskiViewDataSource?
private func draw() {
let bigTriangleRightX : CGFloat = bounds.size.width * scale
let bigTriangleLeftX : CGFloat = bounds.size.width - bigTriangleRightX
let triangleHeight : CGFloat = triangleTipLocation + sqrt(((midPoint(bigTriangleLeftX, point2: bigTriangleRightX) - bigTriangleLeftX)*((midPoint(bigTriangleLeftX, point2: bigTriangleRightX) - bigTriangleLeftX)) * 3))
let triangleTop = CGPoint(x: midPoint(bigTriangleLeftX, point2: bigTriangleRightX), y: triangleTipLocation)
let rightTriangleCorner = CGPoint(x: bigTriangleRightX, y: triangleHeight)
let leftTriangleCorner = CGPoint(x: bigTriangleLeftX, y: triangleHeight)
drawTriangle(leftTriangleCorner, right: rightTriangleCorner, mid: triangleTop, color: .Black)
drawWhiteTriangle(leftTriangleCorner, rightPoint: rightTriangleCorner, topPoint: triangleTop)
}
private func drawWhiteTriangle(leftPoint: CGPoint, rightPoint: CGPoint, topPoint: CGPoint) {
let middle = midPoint(leftPoint.x, point2: rightPoint.x)
let leftCorner = CGPoint(x: midPoint(middle, point2: leftPoint.x), y: midPoint(leftPoint.y, point2: topPoint.y))
let rightCorner = CGPoint(x: midPoint(middle, point2: rightPoint.x), y: midPoint(rightPoint.y, point2: topPoint.y))
let midCorner = CGPoint(x: middle, y: leftPoint.y)
let numberTriangles = dataSource?.getNumTriangles(self) ?? 0
drawTriangle(leftCorner, right: rightCorner, mid: midCorner, color: .White)
drawRecursiveTriangles(leftCorner, rightPoint: rightCorner, middlePoint: midCorner, numTriangles: numberTriangles)
}
private func drawRecursiveTriangles(leftPoint: CGPoint, rightPoint: CGPoint, middlePoint: CGPoint, numTriangles: Int) {
if numTriangles==0 {
return
}
let leftLeftCorner = CGPoint(x: leftPoint.x - (midPoint(leftPoint.x, point2: middlePoint.x)-leftPoint.x), y: midPoint(middlePoint.y, point2: leftPoint.y))
let leftRightCorner = CGPoint(x: midPoint(middlePoint.x, point2: leftPoint.x), y: midPoint(middlePoint.y, point2: leftPoint.y))
let leftMidCorner = CGPoint(x: midPoint(leftLeftCorner.x, point2: leftRightCorner.x), y: middlePoint.y)
drawTriangle(leftLeftCorner, right: leftRightCorner, mid: leftMidCorner, color: .White)
drawRecursiveTriangles(leftLeftCorner, rightPoint: leftRightCorner, middlePoint: leftMidCorner, numTriangles: numTriangles-1)
let rightLeftCorner = CGPoint(x: midPoint(rightPoint.x, point2: middlePoint.x), y: midPoint(leftPoint.y, point2: middlePoint.y))
let rightRightCorner = CGPoint(x: rightPoint.x + (rightPoint.x-midPoint(middlePoint.x, point2: rightPoint.x)), y: midPoint(leftPoint.y, point2: middlePoint.y))
let rightMidCorner = CGPoint(x: midPoint(rightLeftCorner.x, point2:rightRightCorner.x), y: middlePoint.y)
drawTriangle(rightLeftCorner, right: rightRightCorner, mid: rightMidCorner, color: .White)
drawRecursiveTriangles(rightLeftCorner, rightPoint: rightRightCorner, middlePoint: rightMidCorner, numTriangles: numTriangles-1)
let midLeftCorner = CGPoint(x: midPoint(middlePoint.x, point2: leftPoint.x), y: leftPoint.y - (middlePoint.y-midPoint(leftPoint.y, point2: middlePoint.y)))
let midRightCorner = CGPoint(x: midPoint(middlePoint.x, point2: rightPoint.x), y: leftPoint.y - (middlePoint.y-midPoint(leftPoint.y, point2: middlePoint.y)))
let midMidCorner = CGPoint(x: midPoint(leftPoint.x, point2: rightPoint.x), y: leftPoint.y)
drawTriangle(midLeftCorner, right: midRightCorner, mid: midMidCorner, color: .White)
drawRecursiveTriangles(midLeftCorner, rightPoint: midRightCorner, middlePoint: midMidCorner, numTriangles: numTriangles-1)
}
private enum Color {case Black, White}
private func drawTriangle(left: CGPoint, right: CGPoint, mid: CGPoint, color: Color) {
switch color {
case .White : UIColor.whiteColor().set()
case .Black : UIColor.blackColor().set()
}
let path = UIBezierPath()
path.moveToPoint(left)
path.addLineToPoint(right)
path.addLineToPoint(mid)
path.fill()
path.stroke()
}
func scale(gesture: UIPinchGestureRecognizer) {
if gesture.state == .Changed {
scale *= gesture.scale
if scale>=1 {
triangleTipLocation -= 0.5 * gesture.scale
} else {
triangleTipLocation += gesture.scale
}
gesture.scale = 1
}
}
func midPoint(point1: CGFloat, point2: CGFloat) -> CGFloat {
return (point1 + point2) / 2
}
}