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

SwiftUI macOS自定义Menu组件如何移除右侧指示箭头(最低支持11.0)

解决SwiftUI macOS Menu隐藏箭头的方案(兼容macOS 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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.11 09:03:02