自定义PrimitiveButtonStyle在Button生效,为何在.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
相关产品推荐
相关产品推荐

