SwiftUI Modifiers
Modifiers, in SwiftUI, are like styles in other systems. Modifiers simply add more features to a component view. For example, changing the background color of an HStack
to blue or changing its foreground color to white. When you declare a component view you very often give it at least one modifier (I assume padding()
is a top favorite).
Custom Modifiers
You can, of course, create your own modifiers. They behave mostly like the built-in ones, and can be a handy way to avoid replicating the same set of modifiers. For instance, you might want all of your primary buttons to be capsule-shaped, have a green background, with bold white lettering, and with a subtle shadow:
Button("Push Me") { } .padding(10) .background(Capsule().foregroundColor(.green)) .foregroundColor(.white) .font(.system(size: 17, weight: .bold, design: .default)) .clipped() .shadow(radius: 6)
That’s a bunch of code to duplicate each time you want to have a primary-style button in your app, so a custom modifier is the way to go:
struct PrimaryButtonModifier: ViewModifier { func body(content: Content) -> some View { content .padding(10) .background(Capsule().foregroundColor(.green)) .foregroundColor(.white) .font(.system(size: 17, weight: .bold, design: .default)) .clipped() .shadow(radius: 6) } }
And its now easy to apply:
Button("Push Me") { } .modifier(PrimaryButtonModifier())
Packaging a bunch of styling modifiers into a single place is nice and all, but modifiers can be even more helpful. Here are two examples that I’ve recently created.
Tap to Copy
I have this app which displays a bunch of Text
components inside of a Form
. I wanted the user to be able to tap on one and have the text copied to the clipboard. Because I had more than one of these, I created a modifier to do it:
struct CopyModifier: ViewModifier { let field: String @State private var showCopyAlert = false func body(content: Content) -> some View { content .contentShape(Rectangle()) .onTapGesture { UIPasteboard.general.string = field showCopyAlert.toggle() } .alert("Field was Copied", isPresented: $showCopyAlert) { Button("OK", role: .cancel) { } } } }
This modifier has a property, field
, which is the string to copy. The body
of the modifier:
adds a
contentShape
so it has a large tappable area. I do this withHStack
components to make sure even the whitespace in it can be tapped;uses the
onTapGesture
to receive the tap and copy thefield
to the clipboard (akaUIPasteboard
). Then it triggers analert
;adds an
alert
to let the user know the field was indeed copied.
Now you can use it like this:
HStack { Text(data.field1) Spacer() }.modifier(CopyModifier(field: data.field1))
Hide Sheet
In one of my apps, I wanted to make sure any sheet
s being displayed were closed when the app became inactive. In a non-SwiftUI app I might have sub-classed the view and handled the event there, but since we cannot subclass struct
s, the next best thing was a modifier. Since I had a few sheets in the app, I created a modifier to do it:
struct HideSheetModifier: ViewModifier { @Environment(\.presentationMode) var presentationMode @Environment(\.scenePhase) var scenePhase func body(content: Content) -> some View { content .onChange(of: scenePhase) { newPhase in switch newPhase { case .inactive: presentationMode.wrappedValue.dismiss() default: break } } } }
You apply this modifier to the top level View
of your sheet
and it:
uses the
@Environment
object,scenePhase
, to detect when the app is going inactive;uses the
@Environment
object,presentationMode
to dismiss the sheet.
This modifier will only apply to any sheets active, usually one, but if you layer of sheets, this will dismiss each one that has this modifier.
Summary
SwiftUI modifiers are not just for making things look good and simplifying styling. Modifiers can be pretty powerful and really extend the function of Views. Modifiers can use @Environment
and @EnvironmentObject
objects, be passed properties, apply gestures, etc. Since they are structs
you can also make use of @State
and @StateObject
as well as having functions of their own.
I suspect my future apps will have way more modifiers than they do now. I hope yours do too.