0
0
iOS Swiftmobile~20 mins

Custom shapes and paths in iOS Swift - Mini App: Build & Ship

Choose your learning style9 modes available
Build: CustomShapeView
This screen shows a custom drawn shape using paths. The shape is a simple house with a square base and a triangular roof.
Target UI
-------------------
|                 |
|       /\        |
|      /  \       |
|     /____\      |
|     |    |      |
|     |____|      |
|                 |
|  [Draw Shape]   |
-------------------
Create a UIView subclass that draws a house shape using UIBezierPath.
The house should have a square base and a triangular roof.
Add a button labeled 'Draw Shape' below the drawing area.
When the button is tapped, the custom shape should appear in the view.
Use clear and simple colors: black stroke and light gray fill.
Ensure the shape is centered and sized nicely within the view.
Starter Code
iOS Swift
import UIKit

class CustomShapeView: UIView {
    // TODO: Override draw(_:) to draw the custom house shape
}

class ViewController: UIViewController {
    let shapeView = CustomShapeView()
    let drawButton = UIButton(type: .system)

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white

        shapeView.translatesAutoresizingMaskIntoConstraints = false
        shapeView.backgroundColor = .white
        view.addSubview(shapeView)

        drawButton.setTitle("Draw Shape", for: .normal)
        drawButton.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(drawButton)

        NSLayoutConstraint.activate([
            shapeView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            shapeView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 40),
            shapeView.widthAnchor.constraint(equalToConstant: 200),
            shapeView.heightAnchor.constraint(equalToConstant: 200),

            drawButton.topAnchor.constraint(equalTo: shapeView.bottomAnchor, constant: 20),
            drawButton.centerXAnchor.constraint(equalTo: view.centerXAnchor)
        ])

        // TODO: Add target action for drawButton
    }
Task 1
Task 2
Task 3
Solution
iOS Swift
import UIKit

class CustomShapeView: UIView {
    var shouldDrawShape = false

    override func draw(_ rect: CGRect) {
        super.draw(rect)
        guard shouldDrawShape else { return }

        let width = rect.width
        let height = rect.height

        // Draw square base
        let baseRect = CGRect(x: width * 0.2, y: height * 0.5, width: width * 0.6, height: height * 0.4)
        let basePath = UIBezierPath(rect: baseRect)

        UIColor.lightGray.setFill()
        basePath.fill()

        UIColor.black.setStroke()
        basePath.lineWidth = 2
        basePath.stroke()

        // Draw triangular roof
        let roofPath = UIBezierPath()
        roofPath.move(to: CGPoint(x: width * 0.2, y: height * 0.5))
        roofPath.addLine(to: CGPoint(x: width * 0.5, y: height * 0.2))
        roofPath.addLine(to: CGPoint(x: width * 0.8, y: height * 0.5))
        roofPath.close()

        UIColor.lightGray.setFill()
        roofPath.fill()

        UIColor.black.setStroke()
        roofPath.lineWidth = 2
        roofPath.stroke()
    }
}

class ViewController: UIViewController {
    let shapeView = CustomShapeView()
    let drawButton = UIButton(type: .system)

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white

        shapeView.translatesAutoresizingMaskIntoConstraints = false
        shapeView.backgroundColor = .white
        view.addSubview(shapeView)

        drawButton.setTitle("Draw Shape", for: .normal)
        drawButton.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(drawButton)

        NSLayoutConstraint.activate([
            shapeView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            shapeView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 40),
            shapeView.widthAnchor.constraint(equalToConstant: 200),
            shapeView.heightAnchor.constraint(equalToConstant: 200),

            drawButton.topAnchor.constraint(equalTo: shapeView.bottomAnchor, constant: 20),
            drawButton.centerXAnchor.constraint(equalTo: view.centerXAnchor)
        ])

        drawButton.addTarget(self, action: #selector(drawShape), for: .touchUpInside)
    }

    @objc func drawShape() {
        shapeView.shouldDrawShape = true
        shapeView.setNeedsDisplay()
    }
}

We created a CustomShapeView subclass of UIView that overrides the draw(_:) method. It draws a house shape using UIBezierPath: a square base and a triangular roof. We use simple colors: light gray fill and black stroke for clear visibility.

We added a Boolean shouldDrawShape to control when to draw the shape. Initially, the shape is not drawn.

In the ViewController, we set up the layout with Auto Layout constraints. The button labeled "Draw Shape" triggers the drawing by setting shouldDrawShape to true and calling setNeedsDisplay() to refresh the view.

This approach keeps drawing logic inside the custom view and user interaction in the controller, which is a clean separation of concerns.

Final Result
Completed Screen
-------------------
|                 |
|       /\        |
|      /  \       |
|     /____\      |
|     |    |      |
|     |____|      |
|                 |
|  [Draw Shape]   |
-------------------
When the user taps the 'Draw Shape' button, the house shape appears inside the top box area.
The shape is centered and sized nicely with a black outline and light gray fill.
Tapping the button multiple times does not redraw or change the shape.
Stretch Goal
Add a 'Clear' button next to 'Draw Shape' that removes the shape from the view.
💡 Hint
Add another UIButton and an action that sets shouldDrawShape to false and calls setNeedsDisplay() on the shapeView.