Reputation: 1976
Knowing that with SwiftUI view modifiers, order matters - because each modifier is a part of a chain of modifiers, I was wondering if it was possible to reset/overwrite/override a modifier (or the whole chain?
Specifically, I'm wondering about Styles (groupBoxStyle
, buttonStyle
, etc). I have default styles that I want to use in 90% of my app, and a few pages will have slightly different styles for those widgets.
For example:
// Renders a button with the "light" style
Button("Hello world") {
}
.buttonStyle(LightButtonStyle())
.buttonStyle(DarkButtonStyle())
// Renders a button with the "dark" style
Button("Hello world") {
}
.buttonStyle(DarkButtonStyle())
.buttonStyle(LightButtonStyle())
In those cases, I would actually like the 2nd modifier to be used, but the 1st takes over and subsequent styles don't work.
Note: In my actual app, none of my use cases are this trivial - this is just the simplest proof of concept.
The workaround(s) I have are that I create separate LightButton
and DarkButton
views, but that feels very inelegant (and becomes a mess when I have 5-6 variants of each component).
Alternatively, I have a custom MyButton(myStyle: ButtonStyle = .myDefaultStyle)
, but since this is a forms app, there are about 50-60 locations where something like that needs to be updated (instead of applying a modifier at a top level and letting that cascade through).
Edit: I should note, where I can set a top-level style and let it cascade, that works very well and as expected (closer to the View, the modifier takes over). But, there are just some weird use cases where it would be nice to flip the script.
Upvotes: 0
Views: 1250
Reputation: 385590
Generally, buttonStyle
propagates to child views, so ideally you would only need to set your “house style” once on the root view of your app.
The well-known place where this fails to work is the presentation modifiers like .sheet
, which do not propagate styles to the presented view hierarchy. So you will need to write your own versions of the presentation modifiers that re-apply your house style.
For example, here's a custom ButtonStyle
:
struct HouseButtonStyle: ButtonStyle {
func makeBody(configuration: Configuration) -> some View {
configuration.label
.padding(20)
.background {
Capsule(style: .continuous)
.foregroundColor(.pink)
}
.saturation(configuration.isPressed ? 1 : 0.5)
}
}
And here's a cover for sheet
that applies the custom button style to the presented content:
extension View {
func houseSheet<Content: View>(
isPresented: Binding<Bool>,
onDismiss: (() -> Void)? = nil,
@ViewBuilder content: @escaping () -> Content
) -> some View {
return sheet(isPresented: isPresented, onDismiss: onDismiss) {
content()
.buttonStyle(HouseButtonStyle())
}
}
}
We can test out whether a NavigationLink
, a sheet
, and a houseSheet
propagate the button style:
struct ContentView: View {
@State var showingHouseSheet = false
@State var showingStandardSheet = false
var body: some View {
NavigationView {
VStack {
NavigationLink("Navigation Push") {
ContentView()
}
Button("Standard Sheet") {
showingStandardSheet = true
}
Button("House Sheet") {
showingHouseSheet = true
}
}
.sheet(isPresented: $showingStandardSheet) {
ContentView()
}
.houseSheet(isPresented: $showingHouseSheet) {
ContentView()
}
}
}
}
Here's the root view that applies the house button style at the highest level:
struct RootView: View {
var body: some View {
ContentView()
.buttonStyle(HouseButtonStyle())
}
}
If you play with this, you'll find that both NavigationLink
and houseSheet
propagate the button style to the presented content, but sheet
does not.
Upvotes: 1