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

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

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

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

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


当前回答

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

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

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

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

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

其他回答

下面是一个报告正确的第一响应器的解决方案(例如,许多其他解决方案不会报告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

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

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

找到第一响应者的最简单方法:

func sendAction(_ action: Selector, to target: Any?, from sender: Any?, for event: UIEvent?) -> Bool

默认实现将动作方法分派给给定对象 目标对象,如果没有指定目标,则发送给第一个响应器。

下一步:

extension UIResponder
{
    private weak static var first: UIResponder? = nil

    @objc
    private func firstResponderWhereYouAre(sender: AnyObject)
    {
        UIResponder.first = self
    }

    static var actualFirst: UIResponder?
    {
        UIApplication.shared.sendAction(#selector(findFirstResponder(sender:)), to: nil, from: nil, for: nil)
        return UIResponder.first
    }
}

用法: 只需要获取UIResponder。首先是为了你自己的目的。

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

看到这个:

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

遍历可能是第一响应器的视图,并使用- (BOOL)isFirstResponder来确定它们当前是否是。