如何在MacOS应用导航栏添加自定义按钮?参考Xcode导航栏效果
如何在macOS应用中自定义标题栏(添加按钮+调整高度)
我之前也折腾过类似的需求,Xcode那种融合了自定义按钮和加高标题栏的设计确实能提升交互体验,下面分享两种可行的实现方案:
方法一:使用NSTitlebarAccessoryViewController(官方推荐)
这种方式不需要完全替换原生标题栏,而是在标题栏的左侧/右侧插入自定义视图,兼容性更好,也是苹果官方推荐的实现方式:
- 创建自定义附件视图控制器
先写一个继承自NSTitlebarAccessoryViewController的子类,在内部添加你的自定义按钮:
class CustomTitlebarAccessoryVC: NSTitlebarAccessoryViewController { override func loadView() { // 设置附件视图的高度,对应标题栏要加高的尺寸 self.view = NSView(frame: NSRect(x: 0, y: 0, width: 220, height: 48)) self.view.wantsLayer = true self.view.layer?.backgroundColor = NSColor.controlBackgroundColor.cgColor // 添加自定义按钮并绑定点击事件 let customBtn = NSButton(title: "自定义操作", target: self, action: #selector(onCustomBtnClick)) customBtn.frame = NSRect(x: 16, y: 8, width: 120, height: 32) self.view.addSubview(customBtn) } @objc private func onCustomBtnClick() { print("自定义按钮被触发啦") } }
- 将附件控制器挂载到窗口
在你的窗口控制器(NSWindowController)或者窗口初始化逻辑中,添加这个附件控制器并调整标题栏样式:
class MainWindowController: NSWindowController { override func windowDidLoad() { super.windowDidLoad() // 配置标题栏基础样式,让附件视图能正常显示 window?.titlebarAppearsTransparent = true window?.titleVisibility = .hidden window?.toolbarStyle = .unifiedCompact // 创建并添加附件控制器,指定放在标题栏左侧 let accessoryVC = CustomTitlebarAccessoryVC() accessoryVC.layoutAttribute = .leading window?.addTitlebarAccessoryViewController(accessoryVC) // 调整标题栏整体高度,数值为默认高度之上增加的部分 window?.titlebarHeightAdjustment = 16 } }
方法二:完全自定义标题栏(更高自由度)
如果需要像Xcode那样完全掌控标题栏的布局和样式,可以直接隐藏原生标题栏,自己实现一个:
- 隐藏原生标题栏
在窗口初始化时,设置窗口样式以隐藏原生标题栏:
window?.styleMask.insert(.fullSizeContentView) window?.titlebarAppearsTransparent = true window?.titleVisibility = .hidden
- 实现自定义标题栏视图
创建一个NSView子类作为自定义标题栏,添加按钮同时保留窗口拖拽功能:
class CustomTitlebarView: NSView { override func awakeFromNib() { super.awakeFromNib() self.wantsLayer = true self.layer?.backgroundColor = NSColor.controlBackgroundColor.cgColor // 添加左侧自定义按钮 let backBtn = NSButton(title: "返回", target: self, action: #selector(onBackBtnClick)) backBtn.frame = NSRect(x: 16, y: 8, width: 60, height: 32) self.addSubview(backBtn) // 可以在这里添加更多按钮,比如右侧的功能按钮 } // 重写鼠标事件,让自定义标题栏支持窗口拖拽 override func mouseDown(with event: NSEvent) { window?.performDrag(with: event) } @objc private func onBackBtnClick() { print("返回按钮被点击") } }
- 将自定义标题栏添加到窗口布局
在窗口的contentView中插入自定义标题栏,并用Auto Layout固定位置:
// 实例化自定义标题栏 let titlebarView = CustomTitlebarView(frame: NSRect(x: 0, y: 0, width: window?.frame.width ?? 0, height: 48)) window?.contentView?.addSubview(titlebarView) // 设置Auto Layout约束,确保标题栏固定在窗口顶部 titlebarView.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ titlebarView.topAnchor.constraint(equalTo: window!.contentView!.topAnchor), titlebarView.leadingAnchor.constraint(equalTo: window!.contentView!.leadingAnchor), titlebarView.trailingAnchor.constraint(equalTo: window!.contentView!.trailingAnchor), titlebarView.heightAnchor.constraint(equalToConstant: 48) ])
额外提示
- 如果用方法一,
titlebarHeightAdjustment的数值要和附件视图的高度匹配,避免布局错位 - 完全自定义标题栏时,若需要保留原生的最小化/最大化/关闭按钮,可以用
window?.standardWindowButton(.closeButton)获取按钮实例,调整位置后添加到自定义视图中 - 测试时注意不同macOS版本的兼容性,比如Big Sur之后的标题栏样式有细节变化
内容的提问来源于stack exchange,提问作者entangled_photon




