100 Days of SwiftUI Day 43

100 Days of SwiftUI – Day 43

We’ve arrived at day 43 of the 100 Days of SwiftUI! Yesterday, we wrapped up our Moonshot app and completed a few challenges. Today, we’re diving into the third technique project, where we’ll focus on drawing in SwiftUI. Let’s dive in!

Using paths and shapes in SwiftUI

SwiftUI gives us a dedicated Path type for drawing custom shapes. Just like colors, gradients, and shapes, paths are views in their own right. This means we can use them just like text views and images.

//
//  ContentView.swift
//  Drawing
//
//  Created by Darryl Soerdjpal on 08/08/2022.
//

import SwiftUI

struct Triangle: Shape {
    func path(in rect: CGRect) -> Path {
        var path = Path()
        
        path.move(to: CGPoint(x: rect.midX, y: rect.minY))
        path.addLine(to: CGPoint(x: rect.minX, y: rect.maxY)) // min X is the smallest x value in the rectangle, max the greatest value and mid is the horizontal halfway point
        path.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY))
        path.addLine(to: CGPoint(x: rect.midX, y: rect.minY))
        
        return path
    }
}

struct Arc: Shape {
    let startAngle: Angle
    let endAngle: Angle
    let clockwise: Bool
    
    func path(in rect: CGRect) -> Path {
        let rotationAdjustment = Angle.degrees(90)
        let modifiedStart = startAngle - rotationAdjustment
        let modifiedEnd = endAngle - rotationAdjustment
        
        var path = Path()
        
        path.addArc(center: CGPoint(x: rect.midX, y: rect.midY), radius: rect.width / 2, startAngle: modifiedStart, endAngle: modifiedEnd, clockwise: !clockwise)
        
        return path
    }
}

struct ContentView: View {
    var body: some View {
//       Triangle()
//            .fill(.red)
//            .frame(width: 300, height: 300)
        
        Arc(startAngle: .degrees(0), endAngle: .degrees(110), clockwise: true)
            .stroke(.blue, lineWidth: 10)
            .frame(width: 300, height: 300)
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

Adding strokeBorder() support with InsettableShape

When you use the .stroke() modifier, SwiftUI will draw a outline around the shape. With .strokeBorder(), the stroke will be drawn on the inside. As for an InsettableShape:

There is a small but important difference between SwiftUI’s Circle and our Arc: both conform to the Shape protocol, but Circle also conforms to a second protocol called InsettableShape. This is a shape that can be inset – reduced inwards – by a certain amount to produce another shape. The inset shape it produces can be any other kind of insettable shape, but realistically it should be the same shape just in a smaller rectangle.

Hacking with Swift, Paul Hudson (@twostraws)
//
//  ContentView.swift
//  Drawing
//

import SwiftUI

struct Triangle: Shape {
    func path(in rect: CGRect) -> Path {
        var path = Path()
        
        path.move(to: CGPoint(x: rect.midX, y: rect.minY))
        path.addLine(to: CGPoint(x: rect.minX, y: rect.maxY)) // min X is the smallest x value in the rectangle, max the greatest value and mid is the horizontal halfway point
        path.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY))
        path.addLine(to: CGPoint(x: rect.midX, y: rect.minY))
        
        return path
    }
    
    
}

struct Arc: InsettableShape { // InsettableShape inherits from the Shape protocol
    let startAngle: Angle
    let endAngle: Angle
    let clockwise: Bool
    var insetAmount = 0.0
    
    
    func path(in rect: CGRect) -> Path {
        let rotationAdjustment = Angle.degrees(90)
        let modifiedStart = startAngle - rotationAdjustment
        let modifiedEnd = endAngle - rotationAdjustment
        
        var path = Path()
        
        path.addArc(center: CGPoint(x: rect.midX, y: rect.midY), radius: rect.width / 2 - insetAmount, startAngle: modifiedStart, endAngle: modifiedEnd, clockwise: !clockwise)
        
        return path
    }
    
    func inset(by amount: CGFloat) -> some InsettableShape {
        var arc = self
        arc.insetAmount += amount
        return arc
    }
}

struct ContentView: View {
    var body: some View {
//       Triangle()
//            .fill(.red)
//            .frame(width: 300, height: 300)
//
        Arc(startAngle: .degrees(-90), endAngle: .degrees(90), clockwise: true)
            .strokeBorder(.indigo, lineWidth: 40)
            
        
//        Circle()
//            .strokeBorder(.indigo, lineWidth: 40) // SwiftUI will stroke the inside of the border when using strokeBorder()
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

Wrap up

And that’s it for day 43! Tomorrow, we’ll dive further into the drawing functions of SwiftUI, so stay tuned for that. Until then!

Darryl

Hi! My name is Darryl and this is my personal blog where I write about my journey as I learn programming! You'll also find articles about other things that interest me including games, tech and anime.

Post navigation

Leave a Comment

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

100 Days of SwiftUI – Day 85

100 Days of SwiftUI – Day 27 – Machine Learning

100 Days of SwiftUI – Day 25 – Rock, Paper, Scissors

100 Days of SwiftUI – Day 53