8

NSTextField他のいくつかのサブビュー ( 、、WebView...)を含むカスタム ビューを作成しています。サブビューの 1 つが最初の応答者である場合、カスタム ビューに別の境界線を描画させ、メニュー項目とキーボード ショートカットで実行できる単一の項目として機能させたいと思います。次のようになります。

+-------------+
| NSTextField |
+-------------+
| WebView     |
+-------------+

これまでのところ、サブクラスNSTextField化などに成功し、デリゲートが呼び出されたとき- (BOOL)becomeFirstResponderに通知することができました。- (BOOL)resignFirstResponderただし、このアプローチはWebView、それ自体に多くのサブビューが含まれているため、うまくいきません。すべてをサブクラス化することはできません!

サブビューがファーストレスポンダーのステータスを変更したことを検出するより良い方法はありますか? または、カスタム ビューを作成するためのより良い方法はありますか?

4

3 に答える 3

3

-makeFirstResponder:別のアプローチは、メソッドをオーバーライドNSWindowして通知を送信することです。

- (BOOL)makeFirstResponder:(NSResponder *)responder {
  id previous = self.firstResponder ?: [NSNull null];
  id next = responder ?: [NSNull null];

  NSDictionary *userInfo = @{
    BrFirstResponderPreviousKey: previous,
    BrFirstResponderNextKey: next,
  };

  [[NSNotificationCenter defaultCenter] postNotificationName:BrFirstResponderWillChangeNotification object:self userInfo:userInfo];

  return [super makeFirstResponder:responder];
}

その後、カスタム ビューまたはビュー コントローラーで通知をリッスンし、前または次のレスポンダーが必要に応じて使用-isDescendantOf:および設定されたサブビューであるかどうかを確認できneedsDisplayます。

ただし、カスタム ビューはもはや自己完結型ではないため、これは理想的なソリューションではありません。今のところはうまくいきますが、より良いアプローチが共有されることを願っています。

于 2013-08-08T11:04:56.863 に答える
1

iOS / でこの問題が発生UIWebViewmakeFirstResponderましUIWindowた。しかし、Swizzling を使用することで、現在のファーストレスポンダーを取得し、ファーストレスポンダーが変わるたびに通知を投稿できるヘルパー カテゴリを作成することができました。通常、swizzle は最初の goto ではないため、これがすべてパブリック API であるべきかどうかは本当にイライラしますが、これは十分に機能しました。webViewDidEndEditingshouldBeginEditingInDOMRange

まず、カテゴリ ヘッダーを設定します。

@interface UIResponder (Swizzle)
+ (UIResponder *)currentFirstResponder;
- (BOOL)customBecomeFirstResponder;
@end

次に、カテゴリの実装

@implementation UIResponder (Swizzle)
// It's insanity that there is no better way to get a notification when the first responder changes, but there it is.
static UIResponder *sCurrentFirstResponder;
+ (UIResponder *)currentFirstResponder {
    return sCurrentFirstResponder;
}

- (BOOL)customBecomeFirstResponder {
    NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithCapacity:2];
    if(sCurrentFirstResponder) {
        [userInfo setObject:sCurrentFirstResponder forKey:NSKeyValueChangeOldKey];
    }
    sCurrentFirstResponder = self;
    if(sCurrentFirstResponder) {
        [userInfo setObject:sCurrentFirstResponder forKey:NSKeyValueChangeNewKey];
    }
    [[NSNotificationCenter defaultCenter] postNotificationName:kFirstResponderDidChangeNotification
                                                        object:nil
                                                      userInfo:userInfo];
    return [self customBecomeFirstResponder];
}
@end

最後に、JR Swizzleなどのヘルパーを使用して、クラスを交換します。

#import "JRSwizzle.h"

- (void)applicationLoaded {  
    if(![UIResponder jr_swizzleMethod:@selector(becomeFirstResponder) withMethod:@selector(customBecomeFirstResponder) error:&error]) { 
        NSLog(@"Error swizzling - %@",error);
    }
}

共有したいと思いました。プライベート API を使用しないため、App Store で有効です。また、Apple は基本クラスのスウィズリングに対して警告していますが、そうすることに反対する布告はありません。

于 2015-02-17T19:38:30.303 に答える