我在一个多星期前提交了应用,今天收到了可怕的拒绝邮件。它告诉我,我的应用程序不能被接受,因为我使用的是非公共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)

其他回答

操作第一响应器的常用方法是使用nil目标操作。这是一种向响应器链发送任意消息的方式(从第一个响应器开始),并沿着链继续向下,直到有人响应消息(已经实现了与选择器匹配的方法)。

对于取消键盘的情况,这是最有效的方法,无论哪个窗口或视图是第一响应器:

[[UIApplication sharedApplication] sendAction:@selector(resignFirstResponder) to:nil from:nil forEvent:nil];

这应该比[self.view]更有效。窗口endEditing:是的)。

(感谢BigZaphod提醒我这个概念)

如果你的最终目标只是辞职第一个响应者,这应该工作:[self。视图endEditing:是的)

这里是Jakob Egger方法的Swift版本:

import UIKit

private weak var currentFirstResponder: UIResponder?

extension UIResponder {

    static func firstResponder() -> UIResponder? {
        currentFirstResponder = nil
        UIApplication.sharedApplication().sendAction(#selector(self.findFirstResponder(_:)), to: nil, from: nil, forEvent: nil)
        return currentFirstResponder
    }

    func findFirstResponder(sender: AnyObject) {
        currentFirstResponder = self
    }

}

在我的一个应用程序中,如果用户在后台点击,我经常希望第一响应器辞职。为了这个目的,我在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`
}

第一响应者可以是类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。

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

希望能有所帮助。