Skip to content

Latest commit

 

History

History

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 

Day 23: Project 3: Views and Modifiers (Part One)

Follow along at https://www.hackingwithswift.com/100/swiftui/23.

📒 Field Notes

This day covers Part One of Project 3: Views and Modifiers in the 100 Days of SwiftUI Challenge.

It focuses on several specific topics:

  • Views and modifiers: Introduction
  • Why does SwiftUI use structs for views?
  • What is behind the main SwiftUI view?
  • Why modifier order matters
  • Why does SwiftUI use “some View” for its view type?
  • Conditional modifiers
  • Environment modifiers
  • Views as properties
  • View composition
  • Custom modifiers
  • Custom containers

Why does SwiftUI use structs for views?

Many reasons... but the one that stands out to me most is value semantics. Structs force us to focus creating isolated, idempotent, data-driven view -- very analogous to the notion of a pure function, and very much in line with SwiftUI's declarative-, data-driven-, and reactive-view ethos.

What is behind the main SwiftUI view?

Nothing. All views are isolate pieces of UI. At the topmost level, our entry view gets wrapped in a UIHostingController to render within a UIKit scene.

Why modifier order matters

Each modifier creates its own View. This means that successive modifiers are actually modifying the View generated by the previous modifier -- not the base View that they're all chained to.

Why does SwiftUI use “some View” for its view type?

Custom modifiers

If we find ourselves repeating the same chain of modifiers on multiple views, we can group all of these up into a custom modifier by creating a ViewModifier type.

These are similar to custom Views; a slight difference is that ViewModifier types have a body function, rather than a computed property:

struct Watermark: ViewModifier {
    var text: String

    func body(content: Content) -> some View {
        ZStack(alignment: .bottomTrailing) {
            content

            Text(text)
                .font(.caption)
                .foregroundColor(.white)
                .padding(5)
                .background(Color.black)
        }
    }
}

Custom modifiers are applied by using the View.modifier function on a View, and passing in the modifier instance.

We can make this even smoother by extending View directly -- like so:

extension View {
    func watermarked(with text: String) -> some View {
        self.modifier(Watermark(text: text))
    }
}
Color.blue
    .frame(width: 300, height: 200)
    .watermarked(with: "Hacking with Swift")

This is insanely useful -- not just for cleaning up our code, but also for encapsulating design requirements so that they can be applied consistently across our app.

🔗 Additional Related Links