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

自定义PrimitiveButtonStyle在Button生效,为何在.button样式Menu上无效?

自定义按钮样式对.button样式的Menu无效的原因及解决方法

我封装了一个自定义按钮样式,用来根据条件切换原生的bordered和borderedProminent样式,代码如下:

struct CustomButtonStyle: PrimitiveButtonStyle {
    enum Style {
        case bordered
        case borderedProminent
    }
    
    var style: Style
    
    func makeBody(configuration: Configuration) -> some View {
        switch style {
        case .bordered:
            BorderedButtonStyle().makeBody(configuration: configuration)
        case .borderedProminent:
            BorderedProminentButtonStyle().makeBody(configuration: configuration)
        }
    }
}

这个样式在普通Button上能正常工作,但应用到设置了.menuStyle(.button)的Menu时完全无效:

struct ContentView: View {
    @State private var condition: Bool = false
    
    var body: some View {
        NavigationStack {
            List {
                Section {
                    Button("Toggle style", action: { $condition.animation().wrappedValue.toggle() })
                }
                Section {
                    Button("Stylized button", action: {})
                        .buttonStyle(CustomButtonStyle(style: condition ? .borderedProminent : .bordered)) // 此按钮样式正常生效
                }
            }
            .navigationTitle("Toolbar Menu Demo")
            .toolbar {
                Menu("Filter", systemImage: "line.3.horizontal.decrease") {
                    // 菜单内容
                }
                .menuStyle(.button)
                .buttonStyle(CustomButtonStyle(style: condition ? .borderedProminent : .bordered)) // 此Menu样式不生效
                .buttonBorderShape(.circle)
            }
        }
    }
}

奇怪的是,如果直接给Menu应用原生的.buttonStyle(.bordered)或.buttonStyle(.borderedProminent),样式却能正常显示。测试环境为Xcode 15 beta 3。


问题原因

当Menu使用.menuStyle(.button)时,它内部的按钮逻辑遵循的是ButtonStyle协议,而非你自定义样式实现的PrimitiveButtonStyle协议。

苹果原生的.bordered、.borderedProminent样式同时实现了ButtonStyle和PrimitiveButtonStyle两种协议,因此能同时适配普通Button和button样式的Menu;而你的自定义样式仅实现了PrimitiveButtonStyle,自然无法被Menu识别应用。

解决方法

将自定义样式改为实现ButtonStyle协议,或者同时实现两种协议以兼容更多场景:

方案1:仅实现ButtonStyle(满足当前需求)

struct CustomButtonStyle: ButtonStyle {
    enum Style {
        case bordered
        case borderedProminent
    }
    
    var style: Style
    
    func makeBody(configuration: Configuration) -> some View {
        switch style {
        case .bordered:
            BorderedButtonStyle().makeBody(configuration: configuration)
        case .borderedProminent:
            BorderedProminentButtonStyle().makeBody(configuration: configuration)
        }
    }
}

方案2:同时实现两种协议(兼容所有按钮场景)

struct CustomButtonStyle: ButtonStyle, PrimitiveButtonStyle {
    enum Style {
        case bordered
        case borderedProminent
    }
    
    var style: Style
    
    // ButtonStyle 协议实现
    func makeBody(configuration: ButtonStyle.Configuration) -> some View {
        applyStyle(to: configuration)
    }
    
    // PrimitiveButtonStyle 协议实现
    func makeBody(configuration: PrimitiveButtonStyle.Configuration) -> some View {
        switch style {
        case .bordered:
            BorderedButtonStyle().makeBody(configuration: configuration)
        case .borderedProminent:
            BorderedProminentButtonStyle().makeBody(configuration: configuration)
        }
    }
    
    private func applyStyle(to configuration: ButtonStyle.Configuration) -> some View {
        switch style {
        case .bordered:
            BorderedButtonStyle().makeBody(configuration: configuration)
        case .borderedProminent:
            BorderedProminentButtonStyle().makeBody(configuration: configuration)
        }
    }
}

修改后,自定义样式就能同时作用于普通Button和button样式的Menu了。


内容的提问来源于stack exchange,提问作者parapote

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 21:05:54