根据是否禁用更改 SwiftUI 中按钮的颜色
Changing the color of a button in SwiftUI based on disabled or not
我有一个带有发送按钮的文本字段,它是一个 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,它确实会改变图像的颜色。但是,由于状态的变化,您在输入一个字符后将被踢出编辑文本字段,您需要再次 select 文本字段才能继续输入。使用 .disabled 修饰符有更好的方法吗?
试试这个
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 视频中有一个类似的例子。
我猜你想要这个:
您可以为按钮颜色添加计算的 属性,并将 属性 传递给按钮的 foregroundColor
修饰符。您还可以在 HStack
周围使用单个 padding
修饰符,而不是在其子视图上使用单独的 padding
。
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
有两个好处:
启用Button
时,Image
将自动使用环境的accentColor
,禁用Button
时,将自动使用环境的accentColor
。您根本不需要计算颜色。
您可以在 View
层次结构的高层环境中设置 accentColor
,它会滴落到所有后代。这使得为整个界面设置统一的强调色变得容易。
在下面的示例中,我将 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
}
如果您想进一步自定义禁用(或任何其他)状态的按钮,您可以将变量添加到您的自定义 ButtonStyle 结构。
Swift 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))
}
所有修饰符参数都可以是有条件的:
.foregroundColor(condition ? .red : .yellow)
条件可以是任何 true
/false
var condition: Bool { chatMessage.isEmpty } // can be used inline
您可以使用您需要的任何其他条件
有了这些不同的解决方案,您可以使用 \.isEnabled
环境 属性 而不是创建自定义按钮样式或自己传递禁用的布尔值。
@Environment(\.isEnabled) private var isEnabled
我有一个带有发送按钮的文本字段,它是一个 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,它确实会改变图像的颜色。但是,由于状态的变化,您在输入一个字符后将被踢出编辑文本字段,您需要再次 select 文本字段才能继续输入。使用 .disabled 修饰符有更好的方法吗?
试试这个
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 视频中有一个类似的例子。
我猜你想要这个:
您可以为按钮颜色添加计算的 属性,并将 属性 传递给按钮的 foregroundColor
修饰符。您还可以在 HStack
周围使用单个 padding
修饰符,而不是在其子视图上使用单独的 padding
。
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
有两个好处:
启用
Button
时,Image
将自动使用环境的accentColor
,禁用Button
时,将自动使用环境的accentColor
。您根本不需要计算颜色。您可以在
View
层次结构的高层环境中设置accentColor
,它会滴落到所有后代。这使得为整个界面设置统一的强调色变得容易。
在下面的示例中,我将 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
}
如果您想进一步自定义禁用(或任何其他)状态的按钮,您可以将变量添加到您的自定义 ButtonStyle 结构。
Swift 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))
}
所有修饰符参数都可以是有条件的:
.foregroundColor(condition ? .red : .yellow)
条件可以是任何 true
/false
var condition: Bool { chatMessage.isEmpty } // can be used inline
您可以使用您需要的任何其他条件
有了这些不同的解决方案,您可以使用 \.isEnabled
环境 属性 而不是创建自定义按钮样式或自己传递禁用的布尔值。
@Environment(\.isEnabled) private var isEnabled