如何使用 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的发展状况,我猜...
无论如何,问题是这样的:如果您像我一样想要使用 NSSplitViewController
和 NSSplitView
,而 starts 只有一个视图,上面的 subclassing 方法将不起作用,您将从 OP 获得索引错误。有一个私有方法(用于绘制分隔线)假定两个 NSSplitViewItem
一直都在其中,即使 NSSplitView
只用一个就可以正常工作。
我最后做的是 class 对 NSSplitViewController
和 NSSplitView
进行子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 有这个解决方案但没有解释。
我想在 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的发展状况,我猜...
无论如何,问题是这样的:如果您像我一样想要使用 NSSplitViewController
和 NSSplitView
,而 starts 只有一个视图,上面的 subclassing 方法将不起作用,您将从 OP 获得索引错误。有一个私有方法(用于绘制分隔线)假定两个 NSSplitViewItem
一直都在其中,即使 NSSplitView
只用一个就可以正常工作。
我最后做的是 class 对 NSSplitViewController
和 NSSplitView
进行子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 有这个解决方案但没有解释。