macOS侧边栏隐藏时工具栏项异常进入溢出菜单的问题
问题描述
我的NSToolbar包含一个.sidebarTrackingSeparator和一个跟踪第二个分隔线的NSTrackingSeparatorToolbarItem,工具栏被划分为三个区域,中间区域的项已移除以避免杂乱,同时保留了标准文档窗口标题。
- 第二个跟踪分隔线工作正常:面板折叠时,工具栏项仍可见,且有小线条标记跟踪器位置。
- 侧边栏跟踪项存在异常:侧边栏折叠后,其上方的工具栏项(如
.toggleSidebar、.importData)会被移至溢出菜单,但工具栏仍有充足空间,且未显示在窗口标题左侧。 - 特殊现象:若应用启动时侧边栏已折叠,这些项会显示在预期位置;但一旦展开侧边栏再折叠,它们就会消失。
- 尝试过的操作:将系统提供的侧边栏跟踪器替换为自定义
NSTrackingSeparatorToolbarItem,问题依旧。
代理创建工具栏项的代码如下:
extension WindowController: NSToolbarDelegate { func toolbarAllowedItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] { return [.toggleSidebar, .importData, .toggleGrid, .toggleInspector, .sidebarTrackingSeparator, .splitViewTracker] } func toolbarDefaultItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] { return [.toggleSidebar, .importData, .sidebarTrackingSeparator, .toggleGrid, .splitViewTracker, .toggleInspector] } func toolbar(_ toolbar: NSToolbar, itemForItemIdentifier itemIdentifier: NSToolbarItem.Identifier, willBeInsertedIntoToolbar flag: Bool) -> NSToolbarItem? { let item = NSToolbarItem(itemIdentifier: itemIdentifier) switch itemIdentifier { // 其他项的创建逻辑省略 case .splitViewTracker: return NSTrackingSeparatorToolbarItem(identifier: itemIdentifier, splitView: window!.contentViewController!.view.subviews[0] as! NSSplitView, dividerIndex: 1) default: break } return item } }
需要解决的问题:如何确保侧边栏折叠且工具栏有充足空间时,侧边栏相关工具栏项仍显示在工具栏上?
解决方案
1. 手动绑定侧边栏跟踪分隔器到NSSplitView
系统默认的.sidebarTrackingSeparator可能未正确关联侧边栏所在的NSSplitView,建议手动创建绑定到侧边栏的NSTrackingSeparatorToolbarItem:
case .sidebarTrackingSeparator: guard let splitView = window?.contentViewController?.view.subviews.first as? NSSplitView else { return nil } // 假设侧边栏是splitView的第一个子视图,对应分隔器索引为0 return NSTrackingSeparatorToolbarItem(identifier: itemIdentifier, splitView: splitView, dividerIndex: 0)
2. 侧边栏状态变化时强制刷新工具栏布局
在侧边栏折叠/展开的回调中,主动触发工具栏布局更新,避免布局异常:
extension WindowController: NSSplitViewDelegate { func splitViewDidResizeSubviews(_ notification: Notification) { guard let splitView = notification.object as? NSSplitView else { return } // 判断侧边栏是否折叠(以第一个子视图宽度为0作为折叠标志) let sidebarCollapsed = splitView.subviews[0].frame.width == 0 if sidebarCollapsed { window?.toolbar?.validateVisibleItems() window?.layoutIfNeeded() } } }
注意要将当前控制器设置为NSSplitView的代理。
3. 固定侧边栏相关工具栏项的尺寸
给侧边栏关联的工具栏项明确设置最小/最大尺寸,避免被自动压缩或挤入溢出菜单:
case .toggleSidebar: item.image = NSImage(named: NSImage.nameSidebarTemplate) item.action = #selector(toggleSidebar(_:)) item.minSize = NSSize(width: 32, height: 32) item.maxSize = NSSize(width: 32, height: 32) return item case .importData: item.image = NSImage(named: NSImage.nameImportTemplate) item.action = #selector(importData(_:)) item.minSize = NSSize(width: 32, height: 32) item.maxSize = NSSize(width: 32, height: 32) return item
4. 禁用工具栏自动配置
如果上述方法无效,可关闭工具栏的用户自定义和自动保存配置功能,强制保持项的固定顺序:
override func windowDidLoad() { super.windowDidLoad() window?.toolbar?.allowsUserCustomization = false window?.toolbar?.autosavesConfiguration = false }
内容的提问来源于stack exchange,提问作者Remco Poelstra
相关产品推荐
相关产品推荐

