You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.04 20:21:00