【问题标题】:How to inspect the responder chain?如何检查响应者链?
【发布时间】:2010-11-22 01:00:14
【问题描述】:

我正在使用基于文档的架构在单个窗口中处理一些疯狂的多个文档,我已经完成了 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 放入链中)所以我想调试它,但我不确定在哪里看。如何在任何给定时间访问响应者链?

【问题讨论】:

    标签: objective-c cocoa macos nsdocument nsresponder


    【解决方案1】:

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

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

    【讨论】:

    • 谢谢,我不知道我怎么没想到,我非常专注于一次直接获取整个堆栈的想法。
    【解决方案2】:

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

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

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

    printResponderChain(self)
    

    【讨论】:

      【解决方案3】:

      我将通过使用在调试时感觉更“有用”的类方法来改进响应者类别的答案(您不需要中断特定视图或其他任何内容)。

      代码适用于 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
      

      【讨论】:

        【解决方案4】:

        您还可以使用适当的方法向 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
            ...
        }
        

        【讨论】:

          【解决方案5】:

          斯威夫特:

          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)
          

          【讨论】:

            【解决方案6】:

            这是最简单的一个

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

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2020-05-15
              • 1970-01-01
              相关资源
              最近更新 更多