是否有一种可靠的方法来检测子视图中的第一响应者更改?

时间:2013-08-08 09:17:41

标签: objective-c cocoa webview nsview first-responder

我正在构建包含其他几个子视图的自定义视图(NSTextFieldWebView,...)。当其中一个子视图是第一个响应者时,我想让我的自定义视图绘制一个不同的边框,并作为一个可以通过菜单项和键盘快捷键操作的单个项目。它看起来像这样:

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

到目前为止,我已经成功地将NSTextField和其他人子类化,以便在调用- (BOOL)becomeFirstResponder- (BOOL)resignFirstResponder时通知代理人。这种方法不适用于WebView,因为它本身包含许多子视图 - 我不能将它们全部子类化!

是否有更好的方法来检测子视图何时更改其第一响应者状态?或者更好的方法来创建自定义视图?

3 个答案:

答案 0 :(得分:2)

另一种方法是覆盖-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

但这不是一个理想的解决方案,因为自定义视图不再是自包含的。它现在有效,但希望能够分享更好的方法。

答案 1 :(得分:1)

WebViewEditingDelegate 方法将被调用,

辞职第一响应者:

-(void)webViewDidEndEditing:(NSNotification *)notification
{

}

当成为第一响应者时:

-(BOOL)webView:(WebView *)webView shouldBeginEditingInDOMRange:(DOMRange *)range
{
    return YES;
}

答案 2 :(得分:1)

我在iOS / UIWebView中遇到此问题,该问题未在makeFirstResponder中实现UIWindow,也未在webViewDidEndEditingshouldBeginEditingInDOMRange中实施。但是,通过使用Swizzling,我能够创建一个帮助程序类别,允许检索当前的第一响应者,以及每次第一响应者更改时发布通知。真的很沮丧所有这些应该是公共API,但不是,因为混合通常不是第一个goto,但这很好用。

首先,设置类别标题:

@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);
    }
}

以为我会分享。在App Store中有效,因为它不使用私有API,虽然Apple警告不要调整基类,但没有法令禁止这样做。