SwiftUI ButtonStyle在iOS14.4忽略isEnabled环境变量的问题及解决
问题1:iOS14.4和iOS14.5表现不一致的原因
这是iOS14.4及更早版本SwiftUI的原生bug,系统没有将\.isEnabled等环境变量正确注入到自定义ButtonStyle结构体的根层级属性中。苹果在iOS14.5的系统更新中修复了SwiftUI环境变量传递逻辑的缺陷,包括\.isEnabled、\.horizontalSizeClass在内的多个环境变量都在该版本完成了兼容修复。
问题2:你的代码是否存在问题
你的代码完全符合SwiftUI官方开发规范,不存在逻辑错误。代码在iOS14.5+版本运行正常即可证明,异常表现完全由系统层缺陷导致。
问题3:iOS14.4版本的解决方案
有两种可选方案:
- 方案一:将应用最低支持版本升级到iOS14.5,直接规避该系统bug
- 方案二:重写
PrimaryButton实现,绕开系统环境变量注入的缺陷,不需要修改外部调用代码即可兼容iOS14.4。
问题4:兼容iOS14.4的重写代码
核心原理是避免在ButtonStyle的根层级声明@Environment(\.isEnabled),而是将环境变量读取逻辑放到makeBody返回的内部子View中,iOS14.4可以正确识别子View的环境变量注入。
重写后的完整代码如下:
public struct PrimaryButton: ButtonStyle { // 原有代码的ButtonType可按实际需求保留,此处和原实现保持一致 private let type: ButtonType public init() {} public func makeBody(configuration: Configuration) -> some View { // 将样式实现放到内部子View中,读取环境变量 InternalStyleView(config: configuration) } private struct InternalStyleView: View { let config: ButtonStyleConfiguration @Environment(\.isEnabled) private var isEnabled private var padding: EdgeInsets { EdgeInsets(top: 16, leading: 16, bottom: 16, trailing: 16) } private var backgroundColor: Color { isEnabled ? .blue : .gray } private var textColor: Color { isEnabled ? .white : .gray } var body: some View { config.label .font(.callout) .foregroundColor(textColor) .padding(padding) .background(backgroundColor) .clipShape(RoundedRectangle(cornerRadius: 4, style: .continuous)) .opacity(config.isPressed ? 0.5 : 1) } } }
如果不想调整内部结构,也可以选择显式传入启用状态的方案,使用时需要同步传入外部的禁用状态:
// 样式定义 public struct PrimaryButton: ButtonStyle { private let type: ButtonType let isEnabled: Bool public init(isEnabled: Bool) { self.isEnabled = isEnabled } // 其余样式逻辑和原有实现完全一致,仅isEnabled改为外部传入 public func makeBody(configuration: Configuration) -> some View { configuration .label .font(.callout) .foregroundColor(isEnabled ? .white : .gray) .padding(EdgeInsets(top: 16, leading: 16, bottom: 16, trailing: 16)) .background(isEnabled ? .blue : .gray) .clipShape(RoundedRectangle(cornerRadius: 4, style: .continuous)) .opacity(configuration.isPressed ? 0.5 : 1) } } // 使用示例 struct ButtonStyle_Previews: PreviewProvider { static var previews: some View { VStack { SwiftUI.Button(action:{}, label: { Text(verbatim: "Primary") }).buttonStyle(PrimaryButton(isEnabled: true)) SwiftUI.Button(action:{}, label: { Text(verbatim: "PrimaryDisabled") }).buttonStyle(PrimaryButton(isEnabled: false)) .disabled(true) } .padding() } }
内容的提问来源于stack exchange,提问作者Richard Topchii
相关产品推荐
相关产品推荐

