SwiftUI macOS自定义Menu组件如何移除右侧指示箭头(最低支持11.0)
方案1:系统原生API(适配macOS 13及以上)
SwiftUI 从 macOS 13 版本开始提供了官方隐藏菜单箭头的修饰符,直接添加即可,低版本下可以包版本判断不会报错:
Menu { Button(action: { changeLanguage("ko") }) { Text("한국어") } Button(action: { changeLanguage("en") }) { Text("English") } Button(action: { changeLanguage("ja") }) { Text("日本語") } Button(action: { changeLanguage("zh-Hans") }) { Text("中国话") } } label : { Image("globe") } .frame(width: 40, height: 25) .menuStyle(BorderlessButtonMenuStyle()) // 新增内容 #if os(macOS) if #available(macOS 13.0, *) { .menuIndicator(.hidden) } #endif
方案2:偏移裁剪方案(全版本兼容macOS 11.0+,代码最简洁)
如果需要覆盖 macOS 11、12 版本,可以通过偏移把箭头移出组件可视范围后裁剪,实现隐藏效果,无需引入桥接代码:
Menu { // 原有菜单内容完全不变 Button(action: { changeLanguage("ko") }) { Text("한국어") } Button(action: { changeLanguage("en") }) { Text("English") } Button(action: { changeLanguage("ja") }) { Text("日本語") } Button(action: { changeLanguage("zh-Hans") }) { Text("中国话") } } label : { Image("globe") // 向左偏移调整数值,把右侧箭头移出frame范围 .offset(x: 6) } .frame(width: 40, height: 25) .menuStyle(BorderlessButtonMenuStyle()) // 裁剪超出frame的内容,隐藏移出的箭头 .clipped()
偏移数值可以根据你的图标实际尺寸微调,直到箭头完全消失即可。
方案3:AppKit桥接方案(全版本兼容,适合复杂自定义场景)
如果需要更灵活的菜单自定义,可以用NSMenu桥接实现,默认不会生成箭头:
首先定义通用桥接组件:
import SwiftUI import AppKit struct NoArrowMenu<Label: View>: NSViewRepresentable { let menu: NSMenu let label: Label init(menu: NSMenu, @ViewBuilder label: () -> Label) { self.menu = menu self.label = label() } func makeNSView(context: Context) -> NSButton { let button = NSButton(frame: .zero) button.bezelStyle = .borderless button.image = NSHostingView(rootView: label).snapshot() button.imagePosition = .imageOnly button.target = context.coordinator button.action = #selector(Coordinator.triggerMenu(_:)) return button } func updateNSView(_ nsView: NSButton, context: Context) { context.coordinator.menu = menu nsView.image = NSHostingView(rootView: label).snapshot() } func makeCoordinator() -> Coordinator { Coordinator(menu: menu) } class Coordinator: NSObject { var menu: NSMenu init(menu: NSMenu) { self.menu = menu } @objc func triggerMenu(_ sender: NSButton) { menu.popUp(positioning: nil, at: .init(x: 0, y: sender.bounds.maxY + 4), in: sender) } } } extension View { func snapshot() -> NSImage { let hostingView = NSHostingView(rootView: self) hostingView.frame = hostingView.fittingSize let image = NSImage(size: hostingView.bounds.size) image.lockFocus() hostingView.draw(hostingView.bounds) image.unlockFocus() return image } }
替换原有Menu代码:
// 先定义菜单内容和对应逻辑 let langMenu = NSMenu() langMenu.addItem(withTitle: "한국어", action: #selector(changeLang(_:)), keyEquivalent: "") langMenu.addItem(withTitle: "English", action: #selector(changeLang(_:)), keyEquivalent: "") langMenu.addItem(withTitle: "日本語", action: #selector(changeLang(_:)), keyEquivalent: "") langMenu.addItem(withTitle: "中国话", action: #selector(changeLang(_:)), keyEquivalent: "") // 视图部分替换为 NoArrowMenu(menu: langMenu) { Image("globe") } .frame(width: 40, height: 25) // 对应实现触发方法 @objc func changeLang(_ sender: NSMenuItem) { switch sender.title { case "한국어": changeLanguage("ko") case "English": changeLanguage("en") case "日本語": changeLanguage("ja") case "中国话": changeLanguage("zh-Hans") default: break } }
内容的提问来源于stack exchange,提问作者Jun
相关产品推荐
相关产品推荐

