Search code examples
iosswiftuigesturedisable

SwiftUI Get/Set isEnabled when Styling a Tappable Non-Button Component


When styling a Button, the custom ButtonStyle need only get the isEnabled from the environment variable as below.

Custom Button Styling

struct MyStyle: ButtonStyle {
  @Environment(\.isEnabled) var isEnabled

  func makeBody(configuration: Configuration) -> some View {
    if isEnabled {
      do enabled style
    } else {
      do disabled style
    }
}

I have created another style prototype for my custom control which works well, but the the disabled(_:) function does not seem to set the isEnabled for anything but Button.

I need to adjust the styling of the control based on the "enabled" state.

I have tried placing a Button in my custom control instead of using an Image and gesture then use a custom ButtonStyle, but while somewhat unpredictable and seems to only style the Button in the control and nothing else. However the isEnabled is set as expected.

I have also tried using a Button where the Label looks like the new control, but I only want the Button to run the action when the right side is tapped not just anywhere on the button. I am also having difficulty limiting the tappable area of a Button.

I thought I might override the disable(_:) function, but I can't figure out how to override the View extension's function.

While using my new Style and new control I can run disable(_:) on it and it actually disables the gesture on my Image. But does not set the isEnabled in the environment.

Testing the built-in function

struct ContentView: View {

  var body: some View {
    MyControl()
      .controlStyle(MyControlStyle())
      .disabled(true) <- Disables MyControl but does not set isEnabled
  }
}

struct MyControlStyle: ControlStyle {
  @Evironment(\.isEnable) var isEnabled <- Always true even if disabled is true

  func makeBody(configuration: Configuration) -> some View {
    if isEnabled {
      do enabled style
    } else {
      do disabled style
    }
}

Trying to Set isEnabled Directly

struct MyControl: View {
  @State var disabled = false

  func isDisabled(_ disabled: Bool) {
    self.disabled = disabled
    self.environment(\.isEnabled, !disabled)
  }

  var body: some View {
    <Make View>
  }
}

struct ContentView: View {

  var body: some View {
    MyControl()
      .controlStyle(MyControlStyle())
      .isDisabled(true)
  }
}

struct MyControlStyle: ControlStyle {
  @Evironment(\.isEnable) var isEnabled <- Always true even if disabled is true

  func makeBody(configuration: Configuration) -> some View {
    if isEnabled {
      do enabled style
    } else {
      do disabled style
    }
}

I have not figured out what needs to be done to get the isEnabled to function like a button with my custom control.


Solution

  • I finally discovered something that consistently sends the isEnabled to the styling struct. It's not exactly like the ButtonStyle where you retrieve the isEnabled value directly from the @Environment in the styling struct. Instead I placed the @Environment(\.isEnabled) private var isEnabled in my component and passed the isEnabled value as part of the Configuration that is sent to my styling struct.