【问题标题】:Changing the color of a button in SwiftUI based on disabled or not根据禁用与否更改 SwiftUI 中按钮的颜色
【发布时间】:2019-11-02 16:18:03
【问题描述】:

我有一个带有发送按钮的文本字段,它是一个 systemImage 箭头。我希望图像的前景色根据 textField 是否为空而改变。 (即按钮是灰色的,如果文本字段为空则禁用。如果文本字段文本的计数> 1,则为蓝色)。

我有一个不完美的解决方法:


if chatMessageIsValid {
    Spacer()
    HStack {
        TextField($chatMessage, placeholder: Text("Reply"))
            .padding(.leading, 10)
            .textFieldStyle(.roundedBorder)
        Button(action: sendMessage) {
            Image(systemName: "arrow.up.circle")
                .foregroundColor(Color.blue)
                .padding(.trailing, 10)
        }.disabled(!chatMessageIsValid)
    }
} else {
    Spacer()
    HStack {
        TextField($chatMessage, placeholder: Text("Reply"))
            .padding(.leading, 10)
            .textFieldStyle(.roundedBorder)
        Button(action: sendMessage) {
            Image(systemName: "arrow.up.circle")
                .foregroundColor(Color.gray)
                .padding(.trailing, 10)
        }.disabled(!chatMessageIsValid)
    }
}

这几乎可以工作,如果文本长度 > 1,它确实会改变图像的颜色。但是,由于状态的变化,您在输入一个字符后被踢出编辑文本字段,您需要再次选择文本字段才能继续输入。有没有更好的方法来使用 .disabled 修饰符?

【问题讨论】:

  • 将按钮移到它自己的类型中(这样当状态改变时文本字段不会被重绘)并使用@State-binding 模式。

标签: swift swiftui


【解决方案1】:

试试这个

Spacer()
HStack {
    TextField($chatMessage, placeholder: Text("Reply"))
        .padding(.leading, 10)
        .textFieldStyle(.roundedBorder)
    Button(action: sendMessage) {
        Image(systemName: "arrow.up.circle")
            .foregroundColor(chatMessageIsValid ? Color.blue : Color.gray)
            .padding(.trailing, 10)
        }.disabled(!chatMessageIsValid)
}

我想,TextField 失去焦点的原因是在您的代码中,整个视图层次结构会根据chatMessageIsValid 的值进行更改。 SwiftUI 不理解 then 块中的视图层次结构与 else 块中的视图层次结构几乎相同,因此它完全重建它。

在这个编辑过的代码中,它应该看到唯一改变的是图像的foregroundColor 和按钮的disabled 状态,而TextField 保持不变。我相信,Apple 在其中一个 WWDC 视频中也有类似的例子。

【讨论】:

    【解决方案2】:

    我猜你想要这个:

    您可以为按钮颜色添加一个计算属性,并将该属性传递给按钮的foregroundColor 修饰符。您还可以在 HStack 周围使用单个 padding 修饰符,而不是在其子视图上使用单独的 paddings。

    struct ContentView : View {
        @State var chatMessage: String = ""
    
        var body: some View {
            HStack {
                TextField($chatMessage, placeholder: Text("Reply"))
                    .textFieldStyle(.roundedBorder)
                Button(action: sendMessage) {
                    Image(systemName: "arrow.up.circle")
                        .foregroundColor(buttonColor)
                }
                    .disabled(!chatMessageIsValid)
            }
                .padding([.leading, .trailing], 10)
        }
    
        var chatMessageIsValid: Bool {
            return !chatMessage.isEmpty
        }
    
        var buttonColor: Color {
            return chatMessageIsValid ? .accentColor : .gray
        }
    
        func sendMessage() {
            chatMessage = ""
        }
    }
    

    但是,您根本不应该在此处使用 foregroundColor 修饰符。您应该使用 accentColor 修饰符。使用accentColor 有两个好处:

    • Image 将在启用Button 时自动使用环境的accentColor,并在禁用Button 时显示为灰色。您根本不需要计算颜色。

    • 您可以将环境中的accentColor 设置在View 层次结构中的较高位置,它会向下渗透到所有后代。这样可以轻松为整个界面设置统一的强调色。

    在以下示例中,我将accentColor 修饰符放在HStack 上。在实际应用中,您可能会将其设置在整个应用的根视图中:

    struct ContentView : View {
        @State var chatMessage: String = ""
    
        var body: some View {
            HStack {
                TextField($chatMessage, placeholder: Text("Reply"))
                    .textFieldStyle(.roundedBorder)
                Button(action: sendMessage) {
                    Image(systemName: "arrow.up.circle")
                }
                    .disabled(!chatMessageIsValid)
            }
                .padding([.leading, .trailing], 10)
                .accentColor(.orange)
        }
    
        var chatMessageIsValid: Bool {
            return !chatMessage.isEmpty
        }
    
        func sendMessage() {
            chatMessage = ""
        }
    }
    

    另外,Matt 将发送按钮提取为自己的类型的想法可能很聪明。当用户点击它时,它可以很容易地做一些漂亮的事情:

    代码如下:

    struct ContentView : View {
        @State var chatMessage: String = ""
    
        var body: some View {
            HStack {
                TextField($chatMessage, placeholder: Text("Reply"))
                    .textFieldStyle(.roundedBorder)
                SendButton(action: sendMessage, isDisabled: chatMessage.isEmpty)
            }
                .padding([.leading, .trailing], 10)
                .accentColor(.orange)
        }
    
        func sendMessage() {
            chatMessage = ""
        }
    }
    
    struct SendButton: View {
        let action: () -> ()
        let isDisabled: Bool
    
        var body: some View {
                Button(action: {
                    withAnimation {
                        self.action()
                        self.clickCount += 1
                    }
                }) {
                    Image(systemName: "arrow.up.circle")
                        .rotationEffect(.radians(2 * Double.pi * clickCount))
                        .animation(.basic(curve: .easeOut))
                }
                    .disabled(isDisabled)
        }
    
        @State private var clickCount: Double = 0
    }
    

    【讨论】:

    • 太棒了。我真的很喜欢让 SendButton 成为自己的类型。感谢您的帮助!
    • 太棒了! accentColor 修饰符是一个很好的提示!
    • 不错!但是,如果我还设置了前景色,则该颜色会覆盖强调色。那么,如何在普通文本上将accentColor(.orange) 与foregroundColor(.secondary) 结合起来呢?
    【解决方案3】:

    如果您想在禁用(或任何其他)状态下进一步自定义按钮,您可以将变量添加到自定义 ButtonStyle 结构。

    斯威夫特 5.1

    struct CustomButtonStyle: ButtonStyle {
        var disabled = false
        func makeBody(configuration: Self.Configuration) -> some View {
            configuration.label
            .background(disabled ? .red : .blue)
        }
    }
    
    // SwiftUI
    @State var isDisabled = true
    var body: some View {
        Button().buttonStyle(CustomButtonStyle(disabled: self.isDisabled))
    }
    

    【讨论】:

      【解决方案4】:

      所有修饰符参数都可以是有条件的:

      .foregroundColor(condition ? .red : .yellow)
      

      条件可以是任何true/false

      var condition: Bool { chatMessage.isEmpty } // can be used inline
      

      你可以使用任何你需要的条件

      【讨论】:

        【解决方案5】:

        通过这些不同的解决方案,您可以使用\.isEnabled 环境属性,而不是自己创建自定义按钮样式或传入禁用的布尔值。

        @Environment(\.isEnabled) private var isEnabled
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-01-19
          • 2018-03-16
          • 1970-01-01
          • 2018-04-09
          • 1970-01-01
          相关资源
          最近更新 更多