SwiftUI 按钮没有
isEnabled
属性。我如何判断它是否已启用?
在常规 UIKit 中,我会简单地这样做
if button.isEnabeld == true {
} else {
}
但没有 SwiftUI 等效项。
在视图内,如果您希望对
.disabled(true)
设置的状态做出反应,可以使用:
@Environment(\.isEnabled) var isEnabled
由于可以从 View 或 ViewModifier 内部使用环境,因此可以使用它根据外部设置的状态来更改视图的布局属性。
不幸的是,
ButtonStyle
不能直接使用@Environment
,但您可以使用ViewModifier
将环境值注入到ButtonStyle
中,以便使用ButtonStyle
中的值:
// First create a button style that gets the isEnabled value injected
struct MyButtonStyle: ButtonStyle {
private let isEnabled: Bool
init(isEnabled: Bool = true) {
self.isEnabled = isEnabled
}
func makeBody(configuration: Configuration) -> some View {
return configuration
.label
.background(isEnabled ? .green : .gray)
.foregroundColor(isEnabled ? .black : .white)
}
}
// Then make a ViewModifier to inject the state
struct MyButtonModifier: ViewModifier {
@Environment(\.isEnabled) var isEnabled
func body(content: Content) -> some View {
return content.buttonStyle(MyButtonStyle(isEnabled: isEnabled))
}
}
// Then create a convenience function to apply the modifier
extension Button {
func styled() -> some View {
ModifiedContent(content: self, modifier: MyButtonModifier())
}
}
// Finally, try out the button and watch it respond to it's state
struct ContentView: View {
var body: some View {
Button("Test", {}).styled().disabled(true)
}
}
您可以使用此方法将其他内容注入到 ButtonStyle 中,例如尺寸类别和主题。
我将它与自定义样式枚举一起使用,其中包含我们设计系统中找到的所有按钮样式风格。
从外部视图您应该知道是否使用了
.disabled(true)
修饰符。
在视图内部,您可以使用
@Environment(\.isEnabled)
获取该信息:
struct MyButton: View {
let action: () -> Void
@Environment(\.isEnabled) private var isEnabled
var body: some View {
Button(action: action) {
Text("Click")
}
.foregroundColor(isEnabled ? .green : .gray)
}
}
struct MyButton_Previews: PreviewProvider {
static var previews: some View {
VStack {
MyButton(action: {})
MyButton(action: {}).disabled(true)
}
}
}
SwiftUI 的整个想法是避免重复事实来源。你需要以不同的方式思考,并考虑真理的来源在哪里。您需要在此处查找按钮的状态。不是来自按钮本身。
在“通过 SwiftUI 的数据流”中,在 30:50 分钟,他们解释说每条数据都有一个单一事实来源。如果您的按钮从某些 @Binding、@State、@EnvironmentObject 等获取其状态,您的 if 语句也应该从同一位置获取该信息,而不是从按钮。
简短的回答:只需在您的 Button 样式中使用以下内容即可:
@Environment(\.isEnabled) private var isEnabled
更长的答案:
按钮样式:
用途:
@State var isDisabled = false
///.......
Button("Styled button") { isDisabled.toggle() }
.buttonStyle(ButtStyle.BigButton()) // magic inside
.frame(width: 200, height: 50)
.disabled(isDisabled)
Button("switch isDisabled") { isDisabled.toggle() }
源代码:
public struct ButtStyle { }
// Added style to easy stylyng in native way for SwiftUI
@available(macOS 11.0, *)
public extension ButtStyle {
struct BigButton: ButtonStyle {
init() {
}
public func makeBody(configuration: Configuration) -> some View {
BigButtonStyleView(configuration: configuration)
}
}
}
@available(macOS 11.0, *)
struct BigButtonStyleView : View {
let configuration: ButtonStyle.Configuration
@Environment(\.isEnabled) var isEnabled // here we getting "disabled"
@State var hover : Bool = false
var body: some View {
// added animations
MainFrameMod()
.animation(.easeInOut(duration: 0.2), value: hover)
.animation(.easeInOut(duration: 0.2), value: isEnabled)
}
// added opacity on move hover change
// and disabled status
@ViewBuilder
func MainFrameMod() -> some View {
if isEnabled {
MainFrame()
.opacity(hover ? 1 : 0.8)
.onHover{ hover = $0 }
} else {
MainFrame()
.opacity(0.5)
}
}
// Main interface of button
func MainFrame() -> some View {
ZStack {
RoundedRectangle(cornerRadius: 8)
.fill(Color(hex: 0xD8D8D8))
configuration.label
.foregroundColor(.black)
.font(.custom("SF Pro", size: 18))
}
}
}
正如其他开发人员提到的,SwiftUI 的主要思想是 UI 与数据保持同步。您可以通过多种不同的方式执行此操作。这包括@State、@EnvironmentObject、@Binding 等。
struct ContentView: View {
@State private var isEnabled: Bool = false
var body: some View {
VStack {
Button("Press me!") {
}.disabled(isEnabled)
}
.padding()
}
}