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

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

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

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

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


当前回答

在UIResponder上有一个category,你可以合法地要求UIApplication对象告诉你谁是第一个responder。

看到这个:

有没有办法问一个iOS视图它的哪个子视图具有第一响应器状态?

其他回答

在我的一个应用程序中,如果用户在后台点击,我经常希望第一响应器辞职。为了这个目的,我在UIView上写了一个category,我在UIWindow上调用它。

下面的代码基于此,应该返回第一个响应器。

@implementation UIView (FindFirstResponder)
- (id)findFirstResponder
{
    if (self.isFirstResponder) {
        return self;        
    }
    for (UIView *subView in self.subviews) {
        id responder = [subView findFirstResponder];
        if (responder) return responder;
    }
    return nil;
}
@end

iOS 7 +

- (id)findFirstResponder
{
    if (self.isFirstResponder) {
        return self;
    }
    for (UIView *subView in self.view.subviews) {
        if ([subView isFirstResponder]) {
            return subView;
        }
    }
    return nil;
}

迅速:

extension UIView {
    var firstResponder: UIView? {
        guard !isFirstResponder else { return self }

        for subview in subviews {
            if let firstResponder = subview.firstResponder {
                return firstResponder
            }
        }

        return nil
    }
}

在Swift中的使用示例:

if let firstResponder = view.window?.firstResponder {
    // do something with `firstResponder`
}

下面是一个报告正确的第一响应器的解决方案(例如,许多其他解决方案不会报告UIViewController作为第一响应器),不需要在视图层次结构上循环,也不使用私有api。

它利用了苹果的sendAction:to:from:forEvent:方法,该方法已经知道如何访问第一个响应器。

我们只需要在2个方面进行调整:

扩展UIResponder,这样它就可以在第一个responder上执行我们自己的代码。 子类UIEvent来返回第一个响应器。

代码如下:

@interface ABCFirstResponderEvent : UIEvent
@property (nonatomic, strong) UIResponder *firstResponder;
@end

@implementation ABCFirstResponderEvent
@end

@implementation UIResponder (ABCFirstResponder)
- (void)abc_findFirstResponder:(id)sender event:(ABCFirstResponderEvent *)event {
    event.firstResponder = self;
}
@end

@implementation ViewController

+ (UIResponder *)firstResponder {
    ABCFirstResponderEvent *event = [ABCFirstResponderEvent new];
    [[UIApplication sharedApplication] sendAction:@selector(abc_findFirstResponder:event:) to:nil from:nil forEvent:event];
    return event.firstResponder;
}

@end

这是我所做的,当用户单击ModalViewController中的Save/Cancel时,找到什么UITextField是第一个responder:

    NSArray *subviews = [self.tableView subviews];

for (id cell in subviews ) 
{
    if ([cell isKindOfClass:[UITableViewCell class]]) 
    {
        UITableViewCell *aCell = cell;
        NSArray *cellContentViews = [[aCell contentView] subviews];
        for (id textField in cellContentViews) 
        {
            if ([textField isKindOfClass:[UITextField class]]) 
            {
                UITextField *theTextField = textField;
                if ([theTextField isFirstResponder]) {
                    [theTextField resignFirstResponder];
                }

            }
        }

    }

}

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

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

Peter Steinberger刚刚发了关于私有通知UIWindowFirstResponderDidChangeNotification的推特,如果你想观察firstResponder的变化,你可以观察。