如何检查响应链?

发布于 2024-10-03 20:11:24 字数 1983 浏览 4 评论 0原文

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

Current View ->超级视图(重复)->窗口->窗口控制器->文件->文档控制器 ->应用。

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

I'm doing some crazy multiple documents inside a single window stuff with the document-based architecture and I'm 95% done.

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. 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.

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).

From reading the NSResponder documentation, it seems like the chain should be this:

Current View -> Superview (repeat) -> Window -> WindowController -> Document -> DocumentController -> Application.

I'm unsure how the document based architecture is setting up the responder chain (i.e. 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. How do I access the responder chain at any given time?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

我也只是我 2024-10-10 20:11:24

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

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

You can iterate over the responder chain using the nextResponder method of NSResponder. 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);
}
九八野马 2024-10-10 20:11:24

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

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

    print(responder)
    printResponderChain(responder.next)
}

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

printResponderChain(self)

Here is another version for Swift users:

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.

printResponderChain(self)
乖乖兔^ω^ 2024-10-10 20:11:24

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

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

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).

Code is for Cocoa but should be easily portable to 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
日暮斜阳 2024-10-10 20:11:24

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

You can also add a category to class UIResponder with appropriate method that is possible to be used by any subclass of 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
    ...
}
冷情 2024-10-10 20:11:24

迅速:

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)

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)
风追烟花雨 2024-10-10 20:11:24

这是最简单的一个

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

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

    // ...

    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())
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文