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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 20:12:38