3️⃣Views + Modifiers

Fall 2023 | Vin Bui

Structs for Views

Why does SwiftUI use structs for views? First, structs are simpler and faster than classes. But there’s more to it than just performance. In UIKit, every view is a subclass of UIView which is the superclass that contains hundreds properties and methods such as frame, backgroundColor, etc, that we don’t even use them all. You would then create a subclass of UIView and perhaps even a subclass of the subclass and you could keep going forever. Because classes can change their properties freely, things can get messy.

As we can see, classes are too intelligent! We want our views to be dumb and simple. Their only job should just be to convert data into UI. With structs, there are no inherited values. Everything you see is all of it and nothing more.

Views and Modifiers

var body: some View {
      VStack {
          Image(systemName: "globe")
              .imageScale(.large)
              .foregroundColor(.accentColor)
          Text("Hello, world!")
      }
      .padding()
  }

If we take a closer look at the body property of our struct, we can see three different views: VStack, Image, and Text. We will learn more about these views in detail later. You may notice that these views have methods attached to them, such as .padding(). These are known as modifiers. Modifiers return a new view which is an exact replica of our original but with the extra modification. For example, the .padding() modifier on the VStack creates a duplicate of the original VStack but with extra padding. In other words, everything is a view!

Now, notice how these modifiers create a new view. Remember from earlier how the body property has a return type of some View? Well, every time we are adding a modifier to our view, the type that is returned by the property is different. To explore this a bit deeper, we created a Button view that prints out the return type of body when tapped. This Button view has the .padding() and .frame() modifiers added onto it.

var body: some View {
    Button("Hello!") {
        print(type(of: self.body))
    }
    .padding()
    .frame(width: 200, height: 200)
}

This was the output created when we tapped on the button:

ModifiedContent<ModifiedContent<Button<Text>, _PaddingLayout>, _FrameLayout>

Just from looking at this, we can see that the type contains the modifiers that we added onto our view. Now, imagine having a bunch of views, each with different modifiers. The return type can be extremely long! This is the exact reason why the view properties in our struct have a return type of some View.

Last updated