设置:
我正在使用情节提要编写针对 OS X 10.11 的基于文档的应用程序。主窗口有NSToolbar
一个 3-segment NSSegmentedControl
。单击分段控件时,它应该将折叠状态切换为NSSplitViewItem
水平或垂直NSSplitView
。我试图实现的行为与 Xcode 7 中的行为相同,其中工具栏中的分段控件显示/隐藏导航器/调试区域/实用程序视图。
当前,分段控件向第一响应者发送操作。action 方法由一个NSSplitViewController
子类实现,然后切换它的NSSplitViewItem
折叠状态。
问题:
问题是工具栏还包含一个NSSearchField
. 如果NSSearchField
具有焦点,或者即使分段控件本身具有焦点,则NSSegmentedControl
使用光标单击 不会导致操作方法正确地使其沿着响应程序链向上到达NSSplitViewController
子类。
尝试的解决方案:
以前我使用通知而不是目标/操作来解决这个问题,但最终结果过于复杂。另一个想法是将消息发送到窗口控制器,然后将其传递给它的内容视图控制器,它会将它传递给垂直拆分视图控制器,然后将消息(如果需要)发送到水平拆分视图控制器。虽然我知道这会起作用,但它似乎也是一个丑陋的解决方案,必须将代码添加到 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)
}
在窗口控制器中实现分段控制动作方法:
@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)
}
SplitViewTogglable
在 NSSplitViewController 子类中实现协议的方法:
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
}
}