Why a custom ViewModifier is often useless
You’re more of a video kind of person? I’ve got you covered! Here’s a video with the same content than this article 🍿
Advertisement
Authentication is critical, but it shouldn’t slow you down.
Clerk’s iOS SDK gives you secure sign-in, sessions, and user profiles out of the box, so you can focus on shipping great features
Sponsors like Clerk really help me grow my content creation, so if you have time please make sure to check out their survey: it’s a direct support to my content creation ☺️
Let’s imagine that you have this SwiftUI View
, and that you want to encapsulate the use of these two modifiers:
If you follow the instructions from the documentation, this is how you’re likely to implement it.
First, you’ll create a struct
that conforms to the protocol ViewModifier
, and you’ll move the two modifiers inside of it:
And then, inside an extension to View
, you’ll create a convenience method so that it’s easy to call your custom ViewModifier
:
But when you think about it, the struct
that you’ve just created isn’t really useful.
Actually, if you just remove it and move the code directly into the method in the extension of View
, you’ll find that it still works perfectly!
So this begs the question: when do we actually need to create a struct
that conforms to the protocol ViewModifier
?
To understand it, let’s have a look at a different example.
This time we want to encapsulate these 3 modifiers:
Notice how these modifiers rely on properties that are annotated with @State
and @Environment
.
So if we try to directly move the code into a method inside an extension to View
, we’ll quickly notice that it won’t build:
And for a good reason: it’s not possible to declare new stored properties inside an extension.
As you can imagine, it’s exactly in this situation that ViewModifier
becomes necessary!
Because, of course, we are allowed to declare stored properties inside a ViewModifier
, since it’s a struct
.
But we’re also allowed to use the property wrappers @State
and @Environment
, just like if we were inside a View
!
And this time, by using a custom ViewModifier
, our code has been properly encapsulated:
So in a nutshell:
If you only need to encapsulate modifiers that don’t depend on any State
or don’t use the Environment
, you actually don’t need to create a ViewModifier
: a method in an extension of View
will be enough.
However, if you also need to encapsulate state or use property wrappers, then you’ll indeed have to also create a ViewModifier
to handle this.
That’s all for this article, here’s the code if you want to experiment with it!
import SwiftUI
// First Example
extension View {
func rotateAndScale() -> some View {
self
.rotationEffect(.degrees(8))
.scaleEffect(1.2)
}
}
struct ContentView: View {
var body: some View {
VStack {
Text("Hello, world!")
.rotateAndScale()
}
.padding()
}
}
// Second Example
struct HighlightableBorderModifier: ViewModifier {
@Environment(\.colorScheme) private var colorScheme
@State private var isToggled = false
func body(content: Content) -> some View {
content
.padding()
.border(isToggled ? (colorScheme == .dark ? .yellow : .blue) : .gray, width: 3)
.onTapGesture {
isToggled.toggle()
}
}
}
extension View {
func highlightableBorder() -> some View {
modifier(HighlightableBorderModifier())
}
}
struct ContentView: View {
var body: some View {
VStack {
Text("Hello, world!")
.highlightableBorder()
}
.padding()
}
}