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\$
1

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Browse other questions tagged or ask your own question.