如何将工具栏(或其项目)与拆分视图控制器的子项的前端对齐?

时间:2019-03-01 11:47:38

标签: macos cocoa nstoolbar nstoolbaritem nssplitviewcontroller

在iOS中,可以将工具栏添加到任何视图。但是,在macOS中,似乎只能在窗口中添加工具栏。

我正在使用带有工具栏的拆分视图控制器的应用程序,但是工具栏的项目仅与右视图控制器的上下文有关。

例如假设我有某种文本编辑器,其中左窗格显示所有文档(例如在Notes应用程序中),右窗格显示可以编辑的实际文本。格式按钮仅影响右侧窗格中的文本。因此,将工具栏放置在右侧窗格中而不是将其拉伸到整个窗口宽度上似乎非常直观。

Example window with split view controller

有什么方法可以实现这一目标?
(或者是否有很好的UX原因,这将是一种不好的做法?)

我已经注意到Apple在其Notes应用程序中如何通过UX解决此问题:他们仍然使用全角工具栏,但将仅与右窗格相关的按钮项与该窗格的前边缘对齐。

Notes App Screenshot

以防万一,无法将工具栏放置在视图控制器中,如何将工具栏项目与右视图控制器的前端对齐,如上面的屏幕快照所示?


编辑:

根据 TimTwoToes'的答案以及评论中 Willeke 链接的帖子,似乎可以使用“自动布局”将工具栏项目与拆分视图的约束在一起。儿童观。如果工具栏布局固定,此解决方案将起作用。但是,Apple鼓励(有充分的理由)允许用户自定义应用程序的工具栏。

因此,我无法将约束添加到工具栏中的固定项目。相反,可行的解决方案似乎是使用领先的灵活空间并相应地调整其大小。

2 个答案:

答案 0 :(得分:2)

初始注释

事实证明这很棘手,因为有很多事情需要考虑:

  • 自动版式似乎不适用于工具栏项目。 (我读过几篇文章,提到Apple将其归类为错误。)

  • 通常,用户可以自定义应用程序的工具栏(添加和删除项目)。我们不应剥夺用户该选项。

因此,用拆分视图或布局指南仅约束特定的工具栏项目是不可行的(因为该项目可能位于与预期位置不同的位置或根本不在该位置)。

经过数小时的“黑客攻击”,我终于找到了一种可靠的方式来实现所需的行为,而无需使用任何内部/未记录的方法。外观如下:

Example animation of resizing flexible view


操作方法

  1. 创建具有自定义视图的NSToolbarFlexibleSpaceItem,而不是标准的NSToolbarItem。这将充当您灵活的调整大小空间。您可以在代码中或在Interface Builder中完成此操作:

    Toolbar customization in Interface Builder

  2. 为工具栏和灵活的空间(在相应的NSWindowController内)创建出口/属性:

    @IBOutlet weak var toolbar: NSToolbar!
    @IBOutlet weak var tabSpace: NSToolbarItem!
    
  3. 在同一窗口控制器内创建一个用于调整空间宽度的方法:

    private func adjustTabSpaceWidth() {
        for item in toolbar.items {
            if item == tabSpace {
                guard
                    let origin = item.view?.frame.origin,
                    let originInWindowCoordinates = item.view?.convert(origin, to: nil),
                    let leftPane = splitViewController?.splitViewItems.first?.viewController.view
                    else {
                        return
                }
    
                let leftPaneWidth = leftPane.frame.size.width
                let tabWidth = max(leftPaneWidth - originInWindowCoordinates.x, MainWindowController.minTabSpaceWidth)
    
                item.set(width: tabWidth)
            }
        }
    

    }

  4. set(width:)的扩展名中定义NSToolbarItem方法,如下所示:

    private extension NSToolbarItem {
    
        func set(width: CGFloat) {
            minSize = .init(width: width, height: minSize.height)
            maxSize = .init(width: width, height: maxSize.height)
        }
    
    }
    
  5. 使您的窗口控制器符合NSSplitViewDelegate并将其分配给拆分视图的delegate属性。 1 在以下示例中实现以下NSSplitViewDelegate协议方法您的窗口控制器:

    override func splitViewDidResizeSubviews(_ notification: Notification) {
        adjustTabSpaceWidth()
    }
    

这将产生所需的调整大小行为。 (用户仍然可以完全删除该空间或重新放置该空间,但他始终可以将其重新添加到前面。)


1 注意:

如果您使用的是NSSplitViewController,则系统会自动将该控制器分配给其拆分视图的delegate属性,并且您无法更改它。因此,您需要继承NSSplitViewController的子类,覆盖其splitViewDidResizeSubviews()方法,并从那里通知窗口控制器。您可以使用以下代码实现这一点:

protocol SplitViewControllerDelegate: class {
    func splitViewControllerDidResize(_ splitViewController: SplitViewController)
}

class SplitViewController: NSSplitViewController {

    weak var delegate: SplitViewControllerDelegate?

    override func splitViewDidResizeSubviews(_ notification: Notification) {
        delegate?.splitViewControllerDidResize(self)
    }

}

不要忘记将窗口控制器分配为拆分视图控制器的委托:

override func windowDidLoad() {
    super.windowDidLoad()
    splitViewController?.delegate = self
}

并实现各自的委托方法:

extension MainWindowController: SplitViewControllerDelegate {

    func splitViewControllerDidResize(_ splitViewController: SplitViewController) {
        adjustTabSpaceWidth()
    }

}

答案 1 :(得分:1)

没有本地方法可以实现“本地”工具栏。您必须自己创建控件,但是我认为创建控件很简单。

使用自动布局对齐工具栏项目的说明here 。与Mischa描述的自定义工具栏项对齐。

macOS的方法是使用工具栏解决方案并使它们与上下文相关。在这种情况下,文本属性按钮将在右窗格具有焦点时启用,而在其失去焦点时禁用。