2010-11-22 14 views
16

Ich mache einige verrückte mehrere Dokumente in einem einzigen Fenster Zeug mit der Dokument-basierten Architektur und ich bin zu 95% fertig.Wie überprüft man die Responder-Kette?

Ich habe diese zweischichtige Dokumentenarchitektur, wo ein übergeordnetes Dokument das Fenster öffnet und konfiguriert und eine Liste von "untergeordneten" Dokumenten bereitstellt. Wenn der Benutzer eines der untergeordneten Elemente auswählt, wird dieses Dokument mit demselben Fenstercontroller geöffnet, und es wird ein NSTextView in dem Fenster platziert. Die Dokumentzuordnung des Fenster-Controllers wird geändert, so dass der "bearbeitete Punkt" und der Fenstertitel das aktuell ausgewählte Dokument verfolgen. Denken Sie an ein Xcode-Projekt und was passiert, wenn Sie verschiedene Dateien darin bearbeiten.

Um den Code in Pseudoform zu setzen, wird eine Methode wie diese im übergeordneten Dokument aufgerufen, wenn ein untergeordnetes Dokument geöffnet wird.

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

Das funktioniert, und ich weiß, die Fenster-Controller die richtige Dokument Assoziation zu einem bestimmten Zeitpunkt hat, da die Änderung Punkt und Titel folgen je nachdem, was Dokument I Bearbeitung ist.

Wenn ich jedoch auf Speichern klicke (CMD + S oder Datei -> Speichern/Speichern unter), wird das übergeordnete Dokument und nicht das aktuelle Dokument (wie von [[NSDocumentController sharedDocumentController] currentDocument] gemeldet und wie im Fenstertitel und angezeigt) gespeichert Punkt ändern).

Von der NSResponder Dokumentation zu lesen, wie es scheint, die Kette dies sein sollte:

Aktuelle Ansicht -> Superview (Wiederholung) -> Fenster -> WindowController -> Dokument -> Document -> Anwendung.

Ich bin nicht sicher, wie das Dokument basierte Architektur ist die Responder-Kette einrichten zu können (das heißt, wie es NSDocument und NSDocumentController in die Kette platzieren), so würde ich es debuggen möchte, aber ich bin nicht sicher, wo sie suchen müssen. Wie kann ich jederzeit auf die Responder-Kette zugreifen?

Antwort

38

Sie können über die Responder-Kette mit der nextResponder-Methode von NSResponder iterieren. Für Ihr Beispiel sollten Sie mit der aktuellen Ansicht starten können, und dann ausdrucken wiederholt das Ergebnis davon in einer Schleife wie dieser Aufruf:

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

Danke, ich weiß nicht, wie mir das nicht eingefallen ist, ich war so konzentriert auf die Idee, den ganzen Stapel auf einmal zu holen. – d11wtq

6

Hier ist eine weitere Version für Swift-Benutzer:

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

    print(responder) 
    printResponderChain(responder.next) 
} 

Rufen Sie es einfach mit sich selbst an, um die Responder-Kette ausgehend von self auszudrucken.

printResponderChain(self) 
3

können Sie auch eine Kategorie Klasse UIResponder mit geeigneter Methode hinzufügen, die möglich ist, von einer Unterklasse von UIResponder verwendet werden.

@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 

als Sie diese Methode irgendwo im Code als Beispiel unten verwenden:

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

ich ein bisschen auf der Responder Kategorie Antwort verbessern würde, indem Sie eine Klassenmethode verwenden, die sich beim Debuggen "nützlicher" anfühlt (Sie müssen nicht in eine bestimmte Ansicht einbrechen oder was auch immer).

Code ist für Cocoa, sollte aber leicht zu UIKit portierbar sein.

Verwandte Themen