objective c - ¿Cómo inspeccionar la cadena de respuesta?
objective-c cocoa (5)
Estoy haciendo varios documentos locos en una sola ventana con la arquitectura basada en documentos y estoy 95% listo.
Tengo esta arquitectura de documentos de dos niveles, donde un documento principal se abre y configura la ventana, proporcionando una lista de documentos "secundarios". Cuando el usuario selecciona uno de los hijos, ese documento se abre con el mismo controlador de ventana y coloca un NSTextView
en la ventana. La asociación de documentos del controlador de la ventana se cambia de modo que el "punto editado" y el título de la ventana rastreen el documento seleccionado actualmente. Piense en un proyecto de Xcode y en lo que sucede cuando edita diferentes archivos en él.
Para poner el código en pseudoforma, se invoca un método como este en el documento principal cuando se abre un documento secundario.
-(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];
}
}
Esto funciona, y sé que el controlador de la ventana tiene la asociación de documentos correcta en cualquier momento dado que el punto y el título de cambio siguen al documento que esté editando.
Sin embargo, cuando presiono guardar, (CMD + S, o Archivo -> Guardar / Guardar como) quiere guardar el documento principal, no el documento actual (según lo informado por [[NSDocumentController sharedDocumentController] currentDocument]
y como lo indica la ventana título y cambio de punto).
Al leer la documentación de NSResponder
, parece que la cadena debería ser esta:
Vista actual -> Supervisión ( repetición ) -> Ventana -> WindowController -> Documento -> DocumentController -> Aplicación.
No estoy seguro de cómo la arquitectura basada en documentos está configurando la cadena de respondedores (es decir, cómo está colocando NSDocument
y NSDocumentController
en la cadena), así que me gustaría depurarlo, pero no estoy seguro de dónde buscar. ¿Cómo accedo a la cadena de respondedores en un momento dado?
Aquí hay otra versión para usuarios de Swift:
func printResponderChain(_ responder: UIResponder?) {
guard let responder = responder else { return; }
print(responder)
printResponderChain(responder.next)
}
Simplemente llámelo con self para imprimir la cadena de respuesta comenzando desde self.
printResponderChain(self)
Mejoraré un poco la respuesta de la categoría Responder, mediante el uso de un método de clase que se sienta más "utilizable" al depurar (no es necesario interrumpir una vista específica o lo que sea).
El código es para Cocoa pero debería ser fácilmente transportable a 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
Puede iterar sobre la cadena de respuesta utilizando el método nextResponder de NSResponder. Para su ejemplo, debería poder comenzar con la vista actual, y luego imprimir repetidamente el resultado de llamarlo en un bucle como este:
NSResponder *responder = currentView;
while ((responder = [responder nextResponder])) {
NSLog(@"%@", responder);
}
Rápido:
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)
También puede agregar una categoría a la clase UIResponder con el método apropiado que pueda ser usado por cualquier subclase de 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
Puede usar este método en algún lugar del código como se muestra a continuación:
- (void)viewDidLoad {
...
UIView *myView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
[self.view addSubview:myView];
[myView inspectResponderChain]; // UIView is a subclass of UIResponder
...
}