2010-11-22 10 views
16

Robię kilka zwariowanych dokumentów w jednym oknie z architekturą opartą na dokumentach i mam 95%.Jak sprawdzić łańcuch odpowiadający?

Mam tę dwupoziomową architekturę dokumentów, w której dokument nadrzędny otwiera i konfiguruje okno, podając listę dokumentów "podrzędnych". Gdy użytkownik wybierze jedno z elementów podrzędnych, dokument ten zostanie otwarty za pomocą tego samego kontrolera okna i umieści w nim okno NSTextView. Powiązanie dokumentu sterownika okna zostaje zmienione, aby "edytowana kropka" i tytuł okna śledzić wybrany dokument. Pomyśl o projekcie Xcode i o tym, co się dzieje, gdy edytujesz w nim różne pliki.

Aby umieścić kod w pseudo formie, taka metoda jest wywoływana w dokumencie nadrzędnym po otwarciu dokumentu podrzędnego.

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

To działa, i wiem, że kontroler okno ma prawidłowe skojarzenie dokumentu w danym momencie, ponieważ kropka i tytuł zmiana śledzić dowolny dokument jestem edycji.

Jednak po naciśnięciu Zapisz (CMD + S lub Plik -> Zapisz/Zapisz jako) chce zapisać dokument nadrzędny, a nie bieżący dokument (zgodnie z raportem [[NSDocumentController sharedDocumentController] currentDocument] oraz zgodnie z tytułem okna i zmień kropkę).

Od czytanie dokumentacji NSResponder, wydaje się, że łańcuch powinien być tak:

Aktualny Widok -> Superview (powtórz) -> Window -> WindowController -> Dokument -> DocumentController -> Application.

jestem pewien sposób architektura oparta dokument jest utworzenie łańcucha responder (czyli jak to umieszczenie NSDocument i NSDocumentController do łańcucha), więc chciałbym go debugowania, ale nie jestem pewien, gdzie szukać. Jak uzyskać dostęp do łańcucha responderów w danym momencie?

Odpowiedz

38

Można powtórzyć nad łańcuchem odpowiadającego przy użyciu metody NSResponder nextResponder. Na swoim przykładzie, powinieneś być w stanie uruchomić z bieżącego widoku, a następnie wielokrotnie wydrukować wynik nazywając go w pętli jak ta:

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

Dzięki, nie wiem, jak mi to nie przyszło do głowy, byłem tak skoncentrowany na idei bezpośredniego pobierania całego stosu za jednym razem. – d11wtq

6

Oto kolejna wersja dla użytkowników SWIFT:

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

    print(responder) 
    printResponderChain(responder.next) 
} 

Po prostu wywołaj to samo, aby wydrukować łańcuch odpowiadający, zaczynając od siebie.

printResponderChain(self) 
3

Można również dodać kategorię do klasy UIResponder z odpowiedniej metody, która jest możliwa do wykorzystania przez każdego podklasy 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 

niż można użyć tej metody gdzieś w kodzie, jak na poniższym przykładzie:

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

Swift 3:

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


printResponderChain(from: view) 
1

będę poprawić trochę od kategorii obiektu odpowiadającego w odpowiedzi za pomocą metody klasy, która wydaje się być bardziej "użyteczna" podczas debugowania (nie trzeba łamać określonego widoku lub czegoś podobnego).

Kod jest dla kakao, ale powinien być łatwy do przeniesienia do 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 
Powiązane problemy