我在基于文档的架构的单个窗口中做了一些疯狂的多个文档,我完成了95%。
我有这种双层文档架构,其中父文档打开并配置窗口,提供“子”文档列表。当用户选择其中一个子项时,该文档使用相同的窗口控制器打开,并在窗口中放置一个NSTextView
。窗口控制器的文档关联被更改,以便“编辑的点”和窗口标题跟踪当前选择的文档。想想Xcode项目以及编辑其中的不同文件时会发生什么。
要以伪表格形式放置代码,在打开子文档时,会在父文档中调用此类方法。
-(void)openChildDocumentWithURL:(NSURL *)documentURL {
// Don't open the same document multiple times
NSDocument *childDocument = [documentMapTable objectForKey:documentURL];
if (childDocument == nil) {
childDocument = [[[MyDocument alloc] init] autorelease];
// Use the same window controller
// (not as bad as it looks, AppKit swaps the window's document association for us)
[childDocument addWindowController:myWindowController];
[childDocument readFromURL:documentURL ofType:@"Whatever" error:NULL];
// Cache the document
[documentMapTable setObject:childDocument forKey:documentURL];
}
// Make sure the window controller gets the document-association swapped if the doc came from our cache
[myWindowController setDocument:childDocument];
// Swap the text views in
NSTextView *currentTextView = myCurrentTextView;
NSTextView *newTextView = [childDocument textView];
[newTextView setFrame:[currentTextView frame]]; // Don't flicker
[splitView replaceSubview:currentTextView with:newTextView];
if (currentTextView != newTextView) {
[currentTextView release];
currentTextView = [newTextView retain];
}
}
这是有效的,我知道窗口控制器在任何给定时间都有正确的文档关联,因为更改点和标题遵循我正在编辑的文档。
但是,当我点击保存,(CMD + S或文件 - >保存/另存为)时,它想要保存父文档,而不是当前文档(由[[NSDocumentController sharedDocumentController] currentDocument]
报告,并由窗口标题和更改点指示)。
从阅读NSResponder
文档,似乎链应该是这样的:
当前视图 - > Superview(重复) - > Window - > WindowController - > Document - > DocumentController - > Application。
我不确定基于文档的架构是如何设置响应器链的(即如何将NSDocument
和NSDocumentController
放入链中)所以我想调试它,但我不确定在哪里查看。如何在任何给定时间访问响应者链?
您可以使用NSResponder的nextResponder方法迭代响应者链。对于您的示例,您应该能够从当前视图开始,然后重复打印出一个循环调用它的结果,如下所示:
NSResponder *responder = currentView;
while ((responder = [responder nextResponder])) {
NSLog(@"%@", responder);
}
这是Swift用户的另一个版本:
func printResponderChain(_ responder: UIResponder?) {
guard let responder = responder else { return; }
print(responder)
printResponderChain(responder.next)
}
只需用self打电话就可以从self开始打印响应链。
printResponderChain(self)
我将通过使用一种在调试时感觉更“可用”的类方法来改进Responder类别的答案(你不需要在特定的视图或其他任何方面打破)。
代码适用于Cocoa,但应易于移植到UIKit。
@interface NSResponder (Inspect)
+ (void)inspectResponderChain;
@end
@implementation NSResponder (Inspect)
+ (void)inspectResponderChain
{
NSWindow *mainWindow = [NSApplication sharedApplication].mainWindow;
NSLog(@"Responder chain:");
NSResponder *responder = mainWindow.firstResponder;
do
{
NSLog(@"\t%@", [responder debugDescription]);
}
while ((responder = [responder nextResponder]));
}
@end
您还可以使用UIResponder的任何子类可能使用的适当方法向类UIResponder添加类别。
@interface UIResponder (Inspect)
- (void)inspectResponderChain; // show responder chain including self
@end
@implementation UIResponder (Inspect)
- (void)inspectResponderChain
{
UIResponder *x = self;
do {
NSLog(@"%@", x);
}while ((x = [x nextResponder]));
}
@end
您可以在代码中的某处使用此方法,如下例所示:
- (void)viewDidLoad {
...
UIView *myView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
[self.view addSubview:myView];
[myView inspectResponderChain]; // UIView is a subclass of UIResponder
...
}
迅速:
extension UIResponder {
var responderChain: [UIResponder] {
var chain = [UIResponder]()
var nextResponder = next
while nextResponder != nil {
chain.append(nextResponder!)
nextResponder = nextResponder?.next
}
return chain
}
}
// ...
print(self.responderChain)