1

I made a struct that styles my buttons by passing a type and size parameters. I would like the button to change style when it's disabled, but it's not working. The button does toggle between disabled and enabled, but the style is always the enabled style.

struct CustomButton: ButtonStyle {
    
    enum ButtonStyle {
        case button, destructive, light
    }
    enum ButtonSize {
        case normal, large, small
    }
    @Environment(\.isEnabled) var isEnabled
    private var maxWidth : CGFloat
    private var padding : CGFloat
    private var foreground : Color
    private var foregroundDisabled : Color
    private var strokeColor : Color
    private var strokeDisabled : Color
    private var strokeWidth : CGFloat
    private var background : Color
    private var backgroundDisabled : Color
    private var fontSize : Font
    
    init(style: ButtonStyle, size: ButtonSize) {
        switch size {
        case .large:
            self.maxWidth = .infinity
            self.padding = 15.0
            self.fontSize = Font.system(.title3, design: .rounded).weight(.bold)
        case .small:
            self.maxWidth = 200
            self.padding = 8.0
            self.fontSize = Font.system(.callout, design: .rounded)
        default:
            self.maxWidth = .infinity
            self.padding = 12.0
            self.fontSize = Font.system(.body, design: .rounded)
        }
        
        switch style {
        case .light:
            strokeColor = .main
            strokeDisabled = .gray
            strokeWidth = size == .small ? strokeSmall: strokeLarge
            foreground = .main
            foregroundDisabled = .gray
            background = .clear
            backgroundDisabled = .clear
        case .destructive:
            strokeColor = .destructive
            strokeDisabled = .gray
            strokeWidth = size == .small ? strokeSmall : strokeLarge
            foreground = .destructive
            foregroundDisabled = .destructive
            background = .clear
            backgroundDisabled = .clear
        default:
            strokeColor = .clear
            strokeDisabled = .clear
            strokeWidth = 0.0
            foreground = .white
            foregroundDisabled = .white
            backgroundDisabled = .gray
            background = .main
        }
    }
    
    func makeBody(configuration: Self.Configuration) -> some View {
        return configuration.label
            .frame(maxWidth: maxWidth)
            .font(fontSize)
            .foregroundColor(isEnabled ? foreground : Color.red)
            .padding(padding)
            .background(RoundedRectangle(cornerRadius: roundedCorner)
                .strokeBorder(isEnabled ? strokeColor : strokeDisabled, lineWidth: strokeWidth)
                .background(isEnabled ? background : backgroundDisabled)
            )
            .clipShape(RoundedRectangle(cornerRadius: roundedCorner))
            .opacity(configuration.isPressed ? 0.8 : 1.0)
            .scaleEffect(configuration.isPressed ? 0.98 : 1.0)
    }
}

And it's added to a view like this:

Button(action: {   
   //Some logic here
}, label: {
    Text(NSLocalizedString("Add Group", comment: "button"))
})
.disabled(selections.count < 2) //the button does become disabled (but doesn't change style)
.buttonStyle(CustomButton(style: .button, size: .normal))
coopersita
  • 5,011
  • 3
  • 27
  • 48
  • Would this thread help? https://stackoverflow.com/questions/59169436/swiftui-buttonstyle-how-to-check-if-button-is-disabled-or-enabled – Ed Barnes Feb 15 '21 at 19:18

1 Answers1

0

The @Environment is not injected in style, it is only for views, so here a demo of possible solution - based on internal helping wrapper view.

struct CustomButton: ButtonStyle {

    enum ButtonStyle {
        case button, destructive, light
    }
    enum ButtonSize {
        case normal, large, small
    }

    private var maxWidth : CGFloat
    private var padding : CGFloat
    private var foreground : Color
    private var foregroundDisabled : Color
    private var strokeColor : Color
    private var strokeDisabled : Color
    private var strokeWidth : CGFloat
    private var background : Color
    private var backgroundDisabled : Color
    private var fontSize : Font

    init(style: ButtonStyle, size: ButtonSize) {
        switch size {
        case .large:
            self.maxWidth = .infinity
            self.padding = 15.0
            self.fontSize = Font.system(.title3, design: .rounded).weight(.bold)
        case .small:
            self.maxWidth = 200
            self.padding = 8.0
            self.fontSize = Font.system(.callout, design: .rounded)
        default:
            self.maxWidth = .infinity
            self.padding = 12.0
            self.fontSize = Font.system(.body, design: .rounded)
        }

        switch style {
        case .light:
            strokeColor = .main
            strokeDisabled = .gray
            strokeWidth = size == .small ? strokeSmall: strokeLarge
            foreground = .main
            foregroundDisabled = .gray
            background = .clear
            backgroundDisabled = .clear
        case .destructive:
            strokeColor = .destructive
            strokeDisabled = .gray
            strokeWidth = size == .small ? strokeSmall : strokeLarge
            foreground = .destructive
            foregroundDisabled = .destructive
            background = .clear
            backgroundDisabled = .clear
        default:
            strokeColor = .clear
            strokeDisabled = .clear
            strokeWidth = 0.0
            foreground = .white
            foregroundDisabled = .white
            backgroundDisabled = .gray
            background = .main
        }
    }

    private struct EnvReaderView<Content: View>: View {    // << this one !!
        let content: (Bool) -> Content
        @Environment(\.isEnabled) var isEnabled  // read environment
        var body: some View {
            content(isEnabled)  // transfer into builder
        }
    }

    func makeBody(configuration: Self.Configuration) -> some View {
        EnvReaderView { isEnabled in    // now we can use it !!
            configuration.label
                .frame(maxWidth: maxWidth)
                .font(fontSize)
                .foregroundColor(isEnabled ? foreground : Color.red)
                .padding(padding)
                .background(RoundedRectangle(cornerRadius: roundedCorner)
                                    .strokeBorder(isEnabled ? strokeColor : strokeDisabled, lineWidth: strokeWidth)
                                    .background(isEnabled ? background : backgroundDisabled)
                )
                .clipShape(RoundedRectangle(cornerRadius: roundedCorner))
                .opacity(configuration.isPressed ? 0.8 : 1.0)
                .scaleEffect(configuration.isPressed ? 0.98 : 1.0)
        }
    }
}
Asperi
  • 228,894
  • 20
  • 464
  • 690