0
votes

I currently have been using this Environment variable to adjust things for Dark and Light modes on my app.

@Environment(.colorScheme) var colorScheme

The problem I'm having is using that var to conditionally set a button style. This method works just fine.

if colorScheme == .dark {
                    Button("Create Account", action: {
                        
                    }).buttonStyle(CinderDarkButtonStyle(geometry: geometry))
                } else {
                    Button("Create Account", action: {
                        
                    }).buttonStyle(CinderLightButtonStyle(geometry: geometry))
                }

However doing it this way causes me to duplicate code all over the place on a relatively simple user interface. Whenever I attempt to do it this way I end up with errors stating that I have mismatched types.

Button("Create Account", action: {
                            //DO SOME ACTION
                        }).buttonStyle(
colorScheme == .dark ? 
     CinderDarkButtonStyle(geometry: geometry) :
     CinderLightButtonStyle(geometry: geometry)
)
2
Is it the extra )? - Don
@Don no, that was a typo, nice catch :D - xTwisteDx

2 Answers

0
votes

Besides what @Don said about the extra ), CinderDarkButtonStyle and CinderLightButtonStyle are different structs. They aren't the same type.

What you can do is make a custom function that returns an opaque type, as said in this answer. Try something like this:

struct ContentView: View {
    @State var colorScheme = ColorScheme.dark

    var body: some View {
        GeometryReader { geometry in
            Button("Create Account", action: {
                //DO SOME ACTION
            })
            .buttonStyle(for: colorScheme, geometry: geometry) /// use custom buttonStyle function
        }
    }
}

extension Button {
    @ViewBuilder
    func buttonStyle(for colorScheme: ColorScheme, geometry: GeometryProxy) -> some View {
        switch colorScheme {
        case .light:
            buttonStyle(CinderDarkButtonStyle(geometry: geometry))
        case .dark:
            buttonStyle(CinderLightButtonStyle(geometry: geometry))
        }
    }
}

The above switches between a buttonStyle(CinderDarkButtonStyle(geometry: geometry)) and buttonStyle(CinderLightButtonStyle(geometry: geometry)) modifier, based on the colorScheme.

0
votes

You get the error because CinderDarkButtonStyle and CinderLightButtonStyle are, in fact, different types. And, ButtonStyle has an associated type, so you can't just to a straight cast to ButtonStyle.

If this were a normal View and not a ButtonStyle, you could use AnyView to do some type erasure so that the types would be equivalent.

In this case, it seems like it would be much cleaner just to move the logic about which button style to show inside the custom button style:

struct MyTestView : View {
    var body: some View {
        GeometryReader { geometry in
            Button("Create Account", action: {
                //DO SOME ACTION
            })
            .buttonStyle(CinderButtonStyle(geometry: geometry))
        }
    }
}

struct CinderButtonStyle : ButtonStyle {
    var geometry : GeometryProxy
    @Environment(\.colorScheme) private var colorScheme
    
    @ViewBuilder func makeBody(configuration: Configuration) -> some View {
        switch colorScheme {
        case .dark:
            darkBody(configuration: configuration)
        case .light:
            lightBody(configuration: configuration)
        @unknown default:
            lightBody(configuration: configuration)
        }
    }
    
    @ViewBuilder func lightBody(configuration: Configuration) -> some View {
        configuration.label //light modifications
    }
    
    @ViewBuilder func darkBody(configuration: Configuration) -> some View {
        configuration.label //dark modifications
    }
}

Update, using view modifiers:


struct CinderButtonStyle : ButtonStyle {
    var geometry : GeometryProxy
    @Environment(\.colorScheme) private var colorScheme
    
    @ViewBuilder func makeBody(configuration: Configuration) -> some View {
        switch colorScheme {
        case .dark:
            lightAndDarkModifications(configuration: configuration)
                .modifier(DarkModifier())
        default:
            lightAndDarkModifications(configuration: configuration)
                .modifier(LightModifier())
        }
    }
    
    @ViewBuilder func lightAndDarkModifications(configuration: Configuration) -> some View {
        configuration.label
    }
}

struct DarkModifier : ViewModifier {
    func body(content: Content) -> some View {
        content
    }
}

struct LightModifier : ViewModifier {
    func body(content: Content) -> some View {
        content
    }
}