【问题标题】:SwiftUI ButtonStyle - how to check if button is disabled or enabled?SwiftUI ButtonStyle - 如何检查按钮是禁用还是启用?
【发布时间】:2020-03-28 20:48:51
【问题描述】:

根据我的理解,要在 SwiftUI 中设置按钮样式,您可以扩展 ButtonStyle 并实现 func makeBody(configuration: Self.Configuration) -> some View,在其中您开始对 configuration.label 进行修改,这是对 Button 视图的引用。

现在,除了label 字段,ButtonStyle.Configuration 有一个用于isPressed 的布尔字段,但似乎仅此而已。

如何检查按钮是启用还是禁用?

例如,我想在按钮周围画一个边框,如果按钮启用,我希望边框为蓝色,如果禁用,我希望边框为灰色。

我的第一个猜测是这样的:

    func makeBody(configuration: Self.Configuration) -> some View {
        configuration.label
            .overlay(
                RoundedRectangle(cornerRadius: 4).stroke(configuration.isEnabled ? Color.blue : Color.gray, lineWidth: 1).padding(8)
            )
    }

但是没有isEnabled 字段。

Apple 提供的PlainButtonStyle 显然可以访问此信息,因为头文件中的文档注释声明它说“样式可能会应用视觉效果来指示按钮的按下、聚焦或启用状态.'

/// A `Button` style that does not style or decorate its content while idle.
///
/// The style may apply a visual effect to indicate the pressed, focused,
/// or enabled state of the button.
@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
public struct PlainButtonStyle : PrimitiveButtonStyle {
.....

有没有办法访问这些信息?

编辑:

在您建议结束此问题之前,请阅读以下内容:

不久前有人问过类似的问题。该问题未指定任何上下文。

这里我有一个特定的上下文:创建一个通用的可重用按钮样式。

那里提供的答案是不够的。我不能指望人们将禁用状态传递给样式构造函数。这太重复了。

你能想象人们是否必须这样写代码:

Button() { .... }.disabled(someExprssion).buttonStyle(MyCustomStyle(disabled: someExpression))

显然这是不可取的。

很明显,Apple 提供的样式可以访问信息,而无需人们再次将禁用状态传递给该样式。

如果您关闭该问题,您将永远阻止任何人在 StackOverflow 上为该问题提供有用的答案。

建议关闭前请重新考虑。

编辑2:

我猜如果你能得到Button 视图的EnvironmentValues.isEnabled 那么它可能是正确的值。

所以问这个问题的另一种方式是:在 SwiftUI 中是否有可能获得您没有定义自己的视图的环境?

【问题讨论】:

  • 这表明没有办法,但显然有办法,因为内置的 PlainButtonStyle 知道?
  • 不出所料,近身警察开始突袭..
  • 在新建struct NewButtonStyle: ButtonStyle 时,似乎无法抓取按钮的@Environment(\.isEnabled) var isEnabled。只有在创建自定义按钮时才能访问 isEnabled 的正确值...

标签: ios swift swiftui


【解决方案1】:

感谢这个博客,我找到了答案:https://swiftui-lab.com/custom-styling/

您可以通过创建包装视图并在样式结构中使用它来从环境中获取启用状态:

struct MyButtonStyle: ButtonStyle {
    func makeBody(configuration: ButtonStyle.Configuration) -> some View {
        MyButton(configuration: configuration)
    }

    struct MyButton: View {
        let configuration: ButtonStyle.Configuration
        @Environment(\.isEnabled) private var isEnabled: Bool
        var body: some View {
            configuration.label.foregroundColor(isEnabled ? Color.green : Color.red)
        }
    }
}

此示例演示如何获取状态并使用它来更改按钮的外观。如果按钮被禁用,它会将按钮文本颜色更改为红色,如果启用,它将更改为绿色。

【讨论】:

  • 我不得不使用@SwiftUI.Environment,因为我遇到了错误“Enum 'Environment' cannot be used as an attribute”
  • 是的,我预计随着时间的推移和 Swift 的变化,这里的一些关键字将不得不改变..
  • 对于最近来的人来说,没有必要再做这样的玩意儿了,你可以简单地将@Environment 行放在 ButtonStyle 结构中,它会很荣幸。
  • @MichelDonais 你能提供一个代码 sn-p 吗?
  • @Zonker.in.Geneva 看看 Jonathan.'s answer at stackoverflow.com/a/69134893/1060383
【解决方案2】:

对@hasen 的回答进行了一些调整,以使事情更具可配置性

struct MyButtonStyle: ButtonStyle {
    var foreground = Color.white
    var background = Color.blue
    
    func makeBody(configuration: ButtonStyle.Configuration) -> some View {
        MyButton(foreground: foreground, background: background, configuration: configuration)
    }

    struct MyButton: View {
        var foreground:Color
        var background:Color
        let configuration: ButtonStyle.Configuration
        @Environment(\.isEnabled) private var isEnabled: Bool
        var body: some View {
            configuration.label
                .padding(EdgeInsets(top: 7.0, leading: 7.0, bottom: 7.0, trailing: 7.0))
                .frame(maxWidth: .infinity)
                .foregroundColor(isEnabled ? foreground : foreground.opacity(0.5))
                .background(isEnabled ? background : background.opacity(0.5))
                .opacity(configuration.isPressed ? 0.8 : 1.0)
        }
    }
}

用法

       Button(action: {}) {
            Text("Hello")
        }.buttonStyle(MyButtonStyle(foreground: .white, background: .green))

【讨论】:

    【解决方案3】:

    另一种选择是将disabled 布尔值传递给ButtonStyle,然后使用标签上的allowsHitTesting 修饰符来有效地禁用按钮:

    struct MyButtonStyle: ButtonStyle {
        let disabled: Bool
    
        func makeBody(configuration: Self.Configuration) -> some View {
            configuration
                .label
                .allowsHitTesting(!disabled)
        }
    }
    

    【讨论】:

    • 迄今为止最好的解决方案
    【解决方案4】:

    您可以直接在ButtonStyle中通过环境访问启用的值,例如:

    struct MyButtonStyle: ButtonStyle {
        @Environment(\.isEnabled) var isEnabled
        
        func makeBody(configuration: Self.Configuration) -> some View {
            configuration.label
                .background(!isEnabled ? Color.black : (configuration.isPressed ? Color.red : Color.blue))
                
        }
        
    }
    

    【讨论】:

      【解决方案5】:

      您可以将禁用状态合并到buttonStyle中,这样您就不会在应用程序中使用它两次。

        struct CustomizedButtonStyle : PrimitiveButtonStyle {
      
      var disabled: Bool = false
      
      func makeBody(configuration: Self.Configuration) -> some View {
      configuration.label.disabled(disabled)
          .overlay(
              RoundedRectangle(cornerRadius: 4).stroke(disabled ? Color.blue :  Color.gray, lineWidth: 1).padding(8)
          )
      }
      }
      
      
       struct ButtonUpdate: View {
       var body: some View {
          VStack{
          Button(action: {
          }) { Text("button")
          }.buttonStyle(CustomizedButtonStyle(disabled: true))
          Button(action: {
          }) { Text("button")
          }.buttonStyle(CustomizedButtonStyle())
          }}
      
        }
      

      【讨论】:

      • 听起来像是一个合理的解决方法,但这种风格不再只是一种风格,不能轻易地被其他风格取代。此时不妨使用自定义按钮而不是内置的Button
      【解决方案6】:

      回答您的问题:是的,有可能获得您自己没有传递的 Environment 值。 SwiftUI 大量使用 Environment 并且很多 Views 在你不知道的情况下依赖它。

      ButtonStyle 也可以访问Environment,这样您就可以轻松实现您想要的:

      struct SomeButtonStyle: ButtonStyle {
          @Environment(\.isEnabled)
          private var isEnabled: Bool
      
          func makeBody(configuration: Configuration) -> some View {
              configuration.label
                  .disabled(!self.isEnabled)
                  .opacity(self.isEnabled ? 1 : 0.5)
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-11-06
        • 1970-01-01
        • 2014-08-16
        • 2013-09-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多