Подтвердить что ты не робот

Как проверить цепочку ответчиков?

Я делаю несколько сумасшедших нескольких документов внутри одного окна с архитектурой на основе документов, и я делаю 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 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];
  }
}

Это работает, и я знаю, что оконный контроллер имеет правильную ассоциацию документов в любой момент времени, поскольку точка и заголовок изменения следуют независимо от того, какой документ я редактирую.

Однако, когда я нажимаю save (CMD + S или File → Save/Save As), он хочет сохранить родительский документ, а не текущий документ (как сообщается [[NSDocumentController sharedDocumentController] currentDocument] и как указано в заголовке окна и измените точку).

Из чтения документации NSResponder кажется, что цепочка должна быть такой:

Текущий вид → Superview (повтор) → Окно → WindowController → Документ → DocumentController → Приложение.

Я не уверен, как основанная на документе архитектура настраивает цепочку ответчиков (то есть, как она помещает NSDocument и NSDocumentController в цепочку), поэтому я хотел бы отлаживать ее, но я не уверен, где смотреть. Как получить доступ к цепочке ответчиков в любой момент времени?

4b9b3361

Ответ 1

Вы можете перебирать цепочку ответчиков, используя метод nextResponder для NSResponder. Для вашего примера вы можете начать с текущего представления, а затем повторно распечатать результат вызова его в цикле следующим образом:

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

Вы также можете добавить категорию в класс 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

Swift 3:

func printResponderChain(from responder: UIResponder?) {
    var responder = responder
    while let r = responder {
        print(r)
        responder = r.next
    }  
}


printResponderChain(from: view)

Ответ 5

Я немного поправлюсь в ответе категории "Ответчик", используя метод класса, который чувствует себя более "полезным" при отладке (вам не нужно ломать определенный вид или что-то еще).

Код предназначен для 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