レスポンダー チェーンを検査するにはどうすればよいですか?
-
27-09-2019 - |
質問
ドキュメントベースのアーキテクチャを使用して、単一のウィンドウ内で複数のドキュメントを処理していますが、95% 完了しました。
私はこの 2 層ドキュメント アーキテクチャを持っており、親ドキュメントを開いてウィンドウを構成し、「子」ドキュメントのリストを提供します。ユーザーが子のいずれかを選択すると、そのドキュメントは同じウィンドウ コントローラで開かれ、 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
ドキュメントによると、チェーンは次のようになるようです。
現在のビュー -> スーパービュー (繰り返す) -> ウィンドウ -> WindowController -> ドキュメント -> DocumentController -> アプリケーション。
ドキュメントベースのアーキテクチャがレスポンダーチェーンをどのように設定しているかはわかりません(つまり、どのように配置されているか NSDocument
そして NSDocumentController
チェーンに追加する)のでデバッグしたいのですが、どこを調べればよいのかわかりません。いつでもレスポンダー チェーンにアクセスするにはどうすればよいですか?
解決
を使用してレスポンダー チェーンを反復処理できます。 次のレスポンダー NSResponder のメソッド。たとえば、現在のビューから開始して、次のようにループ内で呼び出した結果を繰り返し出力できるはずです。
NSResponder *responder = currentView;
while ((responder = [responder nextResponder])) {
NSLog(@"%@", responder);
}
他のヒント
ここでスウィフトユーザーのための別のバージョンがあります:
func printResponderChain(_ responder: UIResponder?) {
guard let responder = responder else { return; }
print(responder)
printResponderChain(responder.next)
}
単に自己から始まるレスポンダチェーンをプリントアウトするために自己とそれを呼び出します。
printResponderChain(self)
私はレスポンダカテゴリの回答のビットを向上させるだろう、デバッグ時より「使用可能」と感じるクラスメソッドを使用して(あなたが特定のビューまたは任意に分割する必要はありません)。
コードココアのためですが、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
また、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
...
}
スウィフトます:
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)