如何检查响应链?

时间:2010-11-22 01:00:14

标签: objective-c cocoa macos nsdocument nsresponder

我在基于文档的架构的单个窗口中做了一些疯狂的多个文档,我完成了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文档来看,链似乎应该是这样的:

当前视图 - >超级视图(重复) - >窗口 - > WindowController - >文件 - > DocumentController - >应用

我不确定基于文档的体系结构如何设置响应者链(即它如何将NSDocumentNSDocumentController放入链中)所以我想调试它,但我'我不知道在哪里看。如何在任何给定时间访问响应者链?

5 个答案:

答案 0 :(得分:42)

您可以使用NSResponder的nextResponder方法迭代响应者链。对于您的示例,您应该能够从当前视图开始,然后在这样的循环中重复打印出调用它的结果:

NSResponder *responder = currentView;
while ((responder = [responder nextResponder])) {
     NSLog(@"%@", responder);
}

答案 1 :(得分:9)

这是Swift用户的另一个版本:

func printResponderChain(_ responder: UIResponder?) {
    guard let responder = responder else { return; }

    print(responder)
    printResponderChain(responder.next)
}

只需用self调用它即可从self开始打印响应者链。

printResponderChain(self)

答案 2 :(得分:6)

我会通过使用一种在调试时感觉更“可用”的类方法来改进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

答案 3 :(得分:3)

您还可以使用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
    ...
}

答案 4 :(得分:1)

夫特:

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)