我在一个多星期前提交了应用,今天收到了可怕的拒绝邮件。它告诉我,我的应用程序不能被接受,因为我使用的是非公共API;具体来说,它说,

应用程序中包含的非公共API是firstResponder。

现在,违规的API调用实际上是一个解决方案,我发现这里的SO:

UIWindow *keyWindow = [[UIApplication sharedApplication] keyWindow];
UIView   *firstResponder = [keyWindow performSelector:@selector(firstResponder)];

如何在屏幕上显示当前的第一响应器?我正在寻找一种不会让我的应用程序被拒绝的方法。


当前回答

这是一个类别,允许您通过调用[UIResponder currentFirstResponder]快速找到第一个响应器。只需添加以下两个文件到您的项目:

UIResponder + FirstResponder.h:

#import <Cocoa/Cocoa.h>
@interface UIResponder (FirstResponder)
    +(id)currentFirstResponder;
@end

UIResponder + FirstResponder.m:

#import "UIResponder+FirstResponder.h"
static __weak id currentFirstResponder;
@implementation UIResponder (FirstResponder)
    +(id)currentFirstResponder {
         currentFirstResponder = nil;
         [[UIApplication sharedApplication] sendAction:@selector(findFirstResponder:) to:nil from:nil forEvent:nil];
         return currentFirstResponder;
    }
    -(void)findFirstResponder:(id)sender {
        currentFirstResponder = self;
    }
@end

这里的技巧是将一个动作发送给nil将它发送给第一个响应者。

(我最初在这里发表了这个答案:https://stackoverflow.com/a/14135456/322427)

其他回答

这不是很漂亮,但是当我不知道responder是什么时,我辞去firstResponder的方式:

创建一个UITextField,无论是在IB或编程。把它隐藏起来。如果你是用IB写的,就把它链接到你的代码上。

然后,当你想要解散键盘时,你将响应器切换到不可见的文本字段,并立即辞职:

    [self.invisibleField becomeFirstResponder];
    [self.invisibleField resignFirstResponder];

你也可以这样尝试:

- (void) touchesBegan: (NSSet *) touches withEvent: (UIEvent *) event { 

    for (id textField in self.view.subviews) {

        if ([textField isKindOfClass:[UITextField class]] && [textField isFirstResponder]) {
            [textField resignFirstResponder];
        }
    }
} 

我没有试过,但这似乎是个不错的解决办法

下面的代码可以工作。

- (id)ht_findFirstResponder
{
    //ignore hit test fail view
    if (self.userInteractionEnabled == NO || self.alpha <= 0.01 || self.hidden == YES) {
        return nil;
    }
    if ([self isKindOfClass:[UIControl class]] && [(UIControl *)self isEnabled] == NO) {
        return nil;
    }

    //ignore bound out screen
    if (CGRectIntersectsRect(self.frame, [UIApplication sharedApplication].keyWindow.bounds) == NO) {
        return nil;
    }

    if ([self isFirstResponder]) {
        return self;
    }

    for (UIView *subView in self.subviews) {
        id result = [subView ht_findFirstResponder];
        if (result) {
            return result;
        }
    }
    return nil;
}   

第一响应者可以是类UIResponder的任何实例,所以有其他类可能是第一响应者,尽管有UIViews。例如,UIViewController也可能是第一响应器。

在这个要点中,您将找到一种递归方法,通过从应用程序窗口的rootViewController开始循环遍历控制器层次结构来获得第一个响应器。

你可以通过这样来检索第一个响应器

- (void)foo
{
    // Get the first responder
    id firstResponder = [UIResponder firstResponder];

    // Do whatever you want
    [firstResponder resignFirstResponder];      
}

然而,如果第一响应器不是UIView或UIViewController的子类,这种方法将失败。

为了解决这个问题,我们可以采用不同的方法,在UIResponder上创建一个类别,并执行一些神奇的旋转,以便能够构建一个包含该类所有活实例的数组。然后,为了获得第一个responder,我们可以简单地迭代并询问每个对象是否-isFirstResponder。

这种方法可以在另一个要点中找到实现。

希望能有所帮助。

你可以选择下面的UIView扩展来获得它(credit by Daniel):

extension UIView {
    var firstResponder: UIView? {
        guard !isFirstResponder else { return self }
        return subviews.first(where: {$0.firstResponder != nil })
    }
}