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 with HStack components to make sure even the whitespace in it can be tapped;

  • uses the onTapGesture to receive the tap and copy the field to the clipboard (aka UIPasteboard). Then it triggers an alert;

  • 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 sheets 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 structs, 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.

Previous
Previous

No Joy At Christmas

Next
Next

An Introduction to Combine