如何使用 NSSplitViewController 设置自定义 NSSplitView?

How to set custom NSSplitView with NSSplitViewController?

我想在 NSSplitViewController 中使用自定义 NSSplitView

docs 说:

To provide a custom split view, set this property at any time before you call super in the inherited viewDidLoad() method; that is, before the split view controller’s isViewLoaded property is true.

我的 NSSplitViewController 子类称为 MainVC

我尝试在调用 [super viewDidLoad] 之前在 -viewDidLoad 中设置我的自定义拆分视图:

- (void)viewDidLoad {
    self.splitView = [MySplitView new];
    [super viewDidLoad];
    // Rest of viewDidLoad...
}

但是没用。我收到以下错误:

2017-09-02 10:35:43.527312-0700 Zee[6497:632581] ** * Assertion failure in -[MainVC setSplitView:], /BuildRoot/Library/Caches/com.apple.xbs/Sources/AppKit/AppKit- 1561/Controllers/NSSplitViewController.m:220

2017-09-02 10:35:43.527558-0700 Zee[6497:632581] MainVC: The -splitView can only be assigned before the view is loaded

我也试过重写 loadView:

- (void)loadView {
    self.splitView = [MySplitView new];
    [super loadView];
}

但我得到:

2017-09-02 10:39:39.377345-0700 Zee[6575:639146] ** * -[__NSArrayM objectAtIndex:]: index 0 beyond bounds for empty array

如果我在调用 [super loadView] 之后进行赋值,我会得到与我在 -viewDidLoad 中尝试时相同的错误。

如何在我的 NSSplitViewController 子类中使用自定义 NSSplitView

所以索引越界问题与你的拆分​​视图没有任何内容有关,而不是你在初始化时做错了什么。将初始化留在 loadView 中应该没问题。简单地确保你在展示它之前已经用至少 2 个 NSSplitViewItems 初始化了你的 NSSplitViewController 子类。这是一个例子:

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
    MySplitViewController *vc = [MySplitViewController new];
    vc.splitViewItems = @[
        [NSSplitViewItem splitViewItemWithViewController:[MyViewController new]],
        [NSSplitViewItem splitViewItemWithViewController:[MyViewController new]]
    ];
    self.window.contentViewController = vc;
}

该线程中接受的答案在技术上是正确的,但(可以理解)遗漏了 NSSplitViewController 中的一个错误,我在任何地方都没有找到该错误。这就是2017年Cocoa的发展状况,我猜...

无论如何,问题是这样的:如果您像我一样想要使用 NSSplitViewControllerNSSplitView,而 starts 只有一个视图,上面的 subclassing 方法将不起作用,您将从 OP 获得索引错误。有一个私有方法(用于绘制分隔线)假定两个 NSSplitViewItem 一直都在其中,即使 NSSplitView 只用一个就可以正常工作。

我最后做的是 class 对 NSSplitViewControllerNSSplitView 进行子class,并检查我是否创建了一个只有一个视图并在空白处交换的视图NSViewController 加载阶段结束后删除的实例。我还发现直接设置 splitViewController.splitViewItems = ... 效果不佳,您应该调用 addSplitViewItem: 来执行此操作 - 大概有一些您错过的幕后内容。

这个 class 令人恼火的是没有记录,尽管它总体上非常有用。如果你是一个任性的旅行者并且你找到了这个,希望它能有所帮助。

就我而言,这很有效。 (macOS 10.14, Xcode 10)

final class SplitVC: NSSplitViewController {
    private func patch() {
        let v = NSSplitView()
        v.isVertical = true
        v.dividerStyle = .thin
        splitView = v
        splitViewItems = [
            NSSplitViewItem(viewController: NSTabViewController()),
            NSSplitViewItem(viewController: NSTabViewController()),
        ]
    }

    override init(nibName nibNameOrNil: NSNib.Name?, bundle nibBundleOrNil: Bundle?) {
        super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
        patch()
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        patch()
    }
}

我偶然发现了我认为是这个问题的根源。它出现在 splitView(_ splitView: NSSplitView, shouldHideDividerAt dividerIndex: Int) -> Bool 的默认实现中。我认为 Apple 在该方法中的索引不正确,导致越界错误。

要跳过调用它们的实现,只需在您的 NSSplitViewController 子类中覆盖它:

  override func splitView(_ splitView: NSSplitView, shouldHideDividerAt dividerIndex: Int) -> Bool {
    false
  }

如果您改为调用 super.shouldHideDividerAt(...),错误将再次发生:

*** -[__NSArrayM objectAtIndex:]: index 1 beyond bounds [0 .. 0]

其他人提供的添加“虚拟”splitViewItems 的解决方案对我有用,直到我为 insertSplitViewItem 添加了一个覆盖并查看了我的 NSSplitViewController 子类的 .view.frame.splitViewItems,然后 out-of-bounds 错误又回来了,我又被卡住了。

在感到沮丧之后,我去了 GitHub 看看我是否能幸运地找到解决该错误的人。我遇到了这个 comment/code: https://github.com/MoonfishDeFi/Composite/blob/1ccde807c690739b1739e22f02213e195265a992/cotEditor/Document%20Window/Document%20View/SplitViewController.swift#L62 有这个解决方案但没有解释。