NSToolbar 中的 NSSearchField 和 NSSegmentedControl

NSSearchField and NSSegmentedControl inside an NSToolbar

设置:

我正在编写一个基于文档的应用程序,目标是 OS X 10.11 使用故事板。主要 window 有一个 NSToolbar 和一个 3 段 NSSegmentedControl。单击分段控件时,它应该在水平或垂直 NSSplitView 中切换 NSSplitViewItem 的折叠状态。我试图实现的行为与 Xcode 7 中的行为相同,其中工具栏中的分段控件 shows/hides Navigator/Debug Area/Utilities 视图。

目前分段控件向第一响应者发送一个动作。动作方法由 NSSplitViewController 子类实现,然后切换它的 NSSplitViewItem 折叠状态。

问题:

问题是工具栏还包含 NSSearchField。如果 NSSearchField 有焦点,或者即使分段控件本身有焦点,用光标单击 NSSegmentedControl 也不会导致操作方法正确地使其在响应者链上向上到达 NSSplitViewController 子类.

尝试的解决方案:

以前我使用通知而不是 target/action 来解决这个问题,但最后还是太复杂了。另一个想法是将消息发送到 window 控制器,然后控制器将其传递给内容视图控制器,内容视图控制器将其传递给垂直拆分视图控制器,然后将消息(如果需要)发送到水平拆分视图控制器拆分视图控制器。虽然我知道这行得通,但它似乎也是一个丑陋的解决方案,必须将代码添加到 2 个简单地传递消息的附加文件,我认为这是使用响应链避免的。

如有任何见解,我们将不胜感激。

最终解:

我意识到将分段控件的操作连接到第一响应者只有在关键视图上下文很重要的情况下才有意义。在这种情况下,分段控件应切换多个嵌套拆分视图中拆分视图项的折叠状态,而不管关键视图是什么。

定义一个枚举来表示拆分视图的区域:

enum SplitViewArea : Int {
    // The raw values must match the order of the segmented control
    case left, top, right
}

定义一个协议来传达分割视图区域应该被切换:

protocol SplitViewTogglable {
    func toggleSplitViewItem(matching area: SplitViewArea)
}

在window控制器中实现分段控制动作方法:

@IBAction func segmentedControlSelectionStateDidChange(_ sender: Any) {
    guard let segmentedControl = sender as? NSSegmentedControl else { return }
    guard let area = SplitViewArea(rawValue: segmentedControl.selectedSegment) else { return }
    guard let togglable = contentViewController as? SplitViewTogglable else { return }
    togglable.toggleSplitViewItem(matching: area)
}

在NSSplitViewController子类中实现SplitViewTogglable协议的方法:

func toggleSplitViewItem(matching area: SplitViewArea) {
    switch area {
    case .left:
        leftSplitViewItem.isCollapsed = !leftSplitViewItem.isCollapsed
    case .top:
        // Nested NSSplitViewController that adopts SplitViewTogglable
        if let togglable = centerSplitViewItem.viewController as? SplitViewTogglable {
            togglable.toggleSplitViewItem(matching: area)
        }
    case .right:
        rightSplitViewItem.isCollapsed = !rightSplitViewItem.isCollapsed
    }
}

NSSplitViewController是否设置为window的contentViewController? 作为响应者链搜索动作目标的一部分,如果 window 响应动作选择器,它会将其 contentViewController 视为补充目标。

当搜索字段具有关键焦点时,响应链不会通过正常的内容区域,而是通过工具栏到达 window。因此,NSSplitViewController 成为该搜索一部分的唯一方法是 contentViewController.