简体   繁体   English

如何检查响应者链?

[英]How to inspect the responder chain?

I'm doing some crazy multiple documents inside a single window stuff with the document-based architecture and I'm 95% done.我正在使用基于文档的架构在单个窗口中处理一些疯狂的多个文档,并且我已经完成了 95%。

I have this two-tier document architecture, where a parent document opens and configures the window, providing a list of "child" documents.我有这个两层文档架构,其中一个父文档打开并配置窗口,提供“子”文档列表。 When the user selects one of the children, that document is opened with the same window controller and it places a NSTextView in the window.当用户选择其中一个孩子时,该文档将使用相同的窗口控制器打开,并在窗口中放置一个NSTextView The window controller's document association is changed so that the "edited dot" and the window title track the currently selected document.更改窗口控制器的文档关联,以便“编辑点”和窗口标题跟踪当前选定的文档。 Think of an Xcode project and what happens when you edit different files in it.想想一个 Xcode 项目,当你在其中编辑不同的文件时会发生什么。

To put the code in pseudo form, a method like this is invoked in the parent document when a child document is opened.为了将代码置于伪形式,当打开子文档时,在父文档中调用这样的方法。

-(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];
  }
}

This works, and I know the window controller has the correct document association at any given time since the change dot and title follow whichever document I'm editing.这是有效的,我知道窗口控制器在任何给定时间都有正确的文档关联,因为更改点和标题跟随我正在编辑的任何文档。

However, when I hit save, (CMD+S, or File -> Save/Save As) it wants to save the parent document, not the current document (as reported by [[NSDocumentController sharedDocumentController] currentDocument] and as indicated by the window title and change dot).但是,当我点击保存时,(CMD+S 或文件 -> 保存/另存为)它想要保存父文档,而不是当前文档(如[[NSDocumentController sharedDocumentController] currentDocument]和窗口所示)标题和更改点)。

From reading the NSResponder documentation, it seems like the chain should be this:从阅读NSResponder文档NSResponder ,这条链似乎应该是这样的:

Current View -> Superview ( repeat ) -> Window -> WindowController -> Document -> DocumentController -> Application.当前视图 -> Superview(重复) -> Window -> WindowController -> Document -> DocumentController -> Application。

I'm unsure how the document based architecture is setting up the responder chain (ie how it's placing NSDocument and NSDocumentController into the chain) so I'd like to debug it, but I'm not sure where to look.我不确定基于文档的架构是如何设置响应者链的(即它如何将NSDocumentNSDocumentController放入链中),所以我想调试它,但我不确定在哪里查看。 How do I access the responder chain at any given time?如何在任何给定时间访问响应者链?

You can iterate over the responder chain using the nextResponder method of NSResponder.您可以使用NSResponder 的 nextResponder方法遍历响应者链。 For your example, you should be able to start with the current view, and then repeatedly print out the result of calling it in a loop like this:对于您的示例,您应该能够从当前视图开始,然后重复打印出在循环中调用它的结果,如下所示:

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

Here is another version for Swift users:这是 Swift 用户的另一个版本:

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

    print(responder)
    printResponderChain(responder.next)
}

Simply call it with self to print out the responder chain starting from self.只需使用 self 调用它即可打印出从 self 开始的响应者链。

printResponderChain(self)

I'll improve a bit on the Responder category answer, by using a class method which feels more "useable" when debugging (you don't need to break in a specific view or whatever).我将通过使用在调试时感觉更“可用”的类方法(您不需要中断特定视图或其他内容)来改进 Responder 类别答案。

Code is for Cocoa but should be easily portable to UIKit.代码适用于 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

You can also add a category to class UIResponder with appropriate method that is possible to be used by any subclass of UIResponder.您还可以使用适当的方法向 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

Than you can use this method somewhere in code as the example below:您可以在代码中的某处使用此方法,如下例所示:

- (void)viewDidLoad {
    ...
    UIView *myView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
    [self.view addSubview:myView];
    [myView inspectResponderChain]; // UIView is a subclass of UIResponder
    ...
}

Swift:迅速:

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)

Here is the simplest one这里是最简单的

    extension UIResponder {
        func responderChain() -> String {
            guard let next = next else {
                return String(describing: self)
            }

            return String(describing: self) + " -> " + next.responderChain()
        }
    }

    // ...

    print(self.responderChain())

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM