3
\$\begingroup\$

I am a Java programmer trying to learn the ways of Swift. A few days ago, I posted the code for a very simple app that draws a Sierpinski triangle and implements a pretty buggy zoom. I was told to work on organization, delegation, and creating global functions, all of which I tried to do. I also added some new features in the form of a popover view that allows the user to control the number of triangles drawn and the colors they are drawn in.

I am looking for feedback in how I did with my organization, delegation, and global function and if there is anything I can make more "Swift-y" in its implementation.

SierpinskiView

import UIKit

protocol SierpinskiViewDataSource: class {
func NumberTrianglesinSierpinskiTriangle(sender: SierpinskiView) -> Int?
func BackgroundColorForSierpinskiTriangle(sender: SierpinskiView) -> UIColor?
func MainColorForSierpinskiTriangle(sender: SierpinskiView) -> UIColor?
}

func midPoint(point1: CGFloat, point2: CGFloat) -> CGFloat {
return (point1 + point2) / 2
}

@IBDesignable
class SierpinskiView: UIView {

//MARK: - Properties

var scale : CGFloat = 0.95

var triangleTipLocation : CGFloat = 100 {
    didSet {
        if triangleTipLocation < 0 {
            triangleTipLocation = 0
        }
        if triangleTipLocation>100 {
            triangleTipLocation = 100
        }
        setNeedsDisplay()
    }
}

weak var delegate = SierpinskiViewDataSource?()

//MARK - Life Cycle

override func drawRect(rect: CGRect) {
    draw()
}

//MARK - Drawing Functions

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)
    let backColor : UIColor = delegate?.BackgroundColorForSierpinskiTriangle(self) ?? UIColor.blackColor()
    drawTriangle(leftTriangleCorner, right: rightTriangleCorner, mid: triangleTop, color: backColor)
    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 mainColor : UIColor = delegate?.MainColorForSierpinskiTriangle(self) ?? UIColor.whiteColor()
    let numberTrianglesToDraw = delegate?.NumberTrianglesinSierpinskiTriangle(self) ?? 0
    drawTriangle(leftCorner, right: rightCorner, mid: midCorner, color: mainColor)
    drawRecursiveTriangles(leftCorner, rightPoint: rightCorner, middlePoint: midCorner, numTriangles: numberTrianglesToDraw)
}

private func drawRecursiveTriangles(leftPoint: CGPoint, rightPoint: CGPoint, middlePoint: CGPoint, numTriangles: Int) {
    if numTriangles==0 {
        return
    }
    let mainColor : UIColor = delegate?.MainColorForSierpinskiTriangle(self) ?? UIColor.whiteColor()
    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: mainColor)
    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: mainColor)
    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: mainColor)
    drawRecursiveTriangles(midLeftCorner, rightPoint: midRightCorner, middlePoint: midMidCorner, numTriangles: numTriangles-1)
}

private func drawTriangle(left: CGPoint, right: CGPoint, mid: CGPoint, color: UIColor) {
    color.set()
    let path = UIBezierPath()
    path.moveToPoint(left)
    path.addLineToPoint(right)
    path.addLineToPoint(mid)
    path.fill()
    path.stroke()
}

}

SierpinskiViewController

import UIKit

protocol SierpinskiController: class {
func NumberTrianglesinSierpinskiTriangle(sender: SierpinskiViewController) -> Int?
func BackgroundColorForSierpinskiTriangle(sender: SierpinskiViewController) -> UIColor
func MainColorForSierpinskiTriangle(sender: SierpinskiViewController) -> UIColor
func updateUI(sender: SierpinskiViewController)
}

class SierpinskiViewController: UIViewController, UIScrollViewDelegate, SierpinskiViewDataSource, UIPopoverPresentationControllerDelegate {

//MARK: - Properties

@IBOutlet weak var scrollView: UIScrollView!
    {
    didSet {
        scrollView.delegate = self
        scrollView.maximumZoomScale = 20.0
        scrollView.minimumZoomScale = 1.0
    }
}

@IBOutlet weak var sierpinskiView: SierpinskiView! {
    didSet {
        sierpinskiView.delegate = self
    }
}

weak var delegate = SierpinskiController?()

//MARK: - Life Cycle

override func viewDidLoad() {
    super.viewDidLoad()
    scrollView.addSubview(sierpinskiView)
    let widthConstant = CGFloat(100)
    let heightConstant = CGFloat(100)
    scrollView.contentSize = CGSize(width: sierpinskiView.frame.width + widthConstant , height: sierpinskiView.frame.height + heightConstant)
}

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    if let identifier = segue.identifier {
        switch identifier {
        case "controlsSegue":
            if let cvc = segue.destinationViewController as? ControlsViewController {
                if let ppc = cvc.popoverPresentationController {
                    ppc.delegate = self
                }
                cvc.sierpinskiViewController = self
            }
        default: break
        }
    }
}

//MARK: - View Setting Functions

func viewForZoomingInScrollView(scrollView: UIScrollView) -> UIView? {
    return sierpinskiView
}

func adaptivePresentationStyleForPresentationController(controller: UIPresentationController, traitCollection: UITraitCollection) -> UIModalPresentationStyle {
    return UIModalPresentationStyle.None
}

//MARK: - SierpinskiView Protocol Functions

func updateUI () {
    sierpinskiView.setNeedsDisplay()
}

func NumberTrianglesinSierpinskiTriangle(sender: SierpinskiView) -> Int? {
    return delegate?.NumberTrianglesinSierpinskiTriangle(self) ?? 0
}

func BackgroundColorForSierpinskiTriangle(sender: SierpinskiView) -> UIColor? {
    return delegate?.BackgroundColorForSierpinskiTriangle(self) ??  UIColor.blackColor()
}

func MainColorForSierpinskiTriangle(sender: SierpinskiView) -> UIColor? {
    return delegate?.MainColorForSierpinskiTriangle(self) ?? UIColor.whiteColor()
}

}

ControlsViewController

import UIKit

class ControlsViewController: UIViewController, SierpinskiController {

//MARK: - Properties

@IBOutlet weak var controlsView: UIView!

@IBOutlet weak var BackgroundColorButton: UIButton!

@IBOutlet weak var MainColorButton: UIButton!

@IBOutlet weak var numberOfTrianglesLabel: UILabel!

@IBOutlet weak var numberOfTrianglesSlider: UISlider!

private var mainColorSelected = false {
    didSet {
        switch mainColorSelected {
        case true: MainColorButton.setTitleColor(UIColor.redColor(), forState: .Normal)
        BackgroundColorButton.setTitleColor(UIColor.blueColor(), forState: .Normal)
        case false: BackgroundColorButton.setTitleColor(UIColor.redColor(), forState: .Normal)
        MainColorButton.setTitleColor(UIColor.blueColor(), forState: .Normal)
        }
    }
}

var sierpinskiViewController = SierpinskiViewController?() {
    didSet {
        sierpinskiViewController?.delegate = self
        updateUI(sierpinskiViewController!)
    }
}

var mainColor : UIColor = UIColor.whiteColor() {
    didSet {
        updateUI(sierpinskiViewController!)
    }
}

var backgroundColor: UIColor = UIColor.blackColor() {
    didSet {
        updateUI(sierpinskiViewController!)
    }
}

var numberTriangles : Int = 0 {
    didSet {
        updateUI(sierpinskiViewController!)
    }
}

//MARK: - Life Cycle

override func viewDidLoad() {
    super.viewDidLoad()
    mainColorSelected = false
}

//MARK: - SierpinskiView Setting Functions

@IBAction func backgroundColorSelected(sender: UIButton) {
    mainColorSelected = false
}

@IBAction func mainColorSelected(sender: UIButton) {
    mainColorSelected = true
}

@IBAction func changeNumberOfTriangles(sender: UISlider) {
    let currentValue = Int(sender.value)
    numberOfTrianglesLabel.text = "\(currentValue)"
    numberTriangles = currentValue
}

@IBAction func changeColor(sender: AnyObject) {
    let color = sender.currentTitle!
    switch mainColorSelected {
    case true:
        switch color! {
        case "R": mainColor = UIColor.redColor()
        case "O": mainColor = UIColor.orangeColor()
        case "Y": mainColor = UIColor.yellowColor()
        case "G": mainColor = UIColor.greenColor()
        case "B": mainColor = UIColor.blueColor()
        case "P": mainColor = UIColor.purpleColor()
        case "Bl": mainColor = UIColor.blackColor()
        case "W": mainColor = UIColor.whiteColor()
        default: break
        }
    case false:
        switch color! {
        case "R": backgroundColor = UIColor.redColor()
        case "O": backgroundColor = UIColor.orangeColor()
        case "Y": backgroundColor = UIColor.yellowColor()
        case "G": backgroundColor = UIColor.greenColor()
        case "B": backgroundColor = UIColor.blueColor()
        case "P": backgroundColor = UIColor.purpleColor()
        case "Bl": backgroundColor = UIColor.blackColor()
        case "W": backgroundColor = UIColor.whiteColor()
        default: break
        }
    }
}

//MARK: - SierpinskiController Protocol Functions

func NumberTrianglesinSierpinskiTriangle(sender: SierpinskiViewController) -> Int? {
    return numberTriangles
}

func MainColorForSierpinskiTriangle(sender: SierpinskiViewController) -> UIColor {
    return mainColor
}

func BackgroundColorForSierpinskiTriangle(sender: SierpinskiViewController) -> UIColor {
    return backgroundColor
}

func updateUI(sender: SierpinskiViewController) {
    sender.updateUI()
}
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.