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

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

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

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

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


当前回答

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

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

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

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

其他回答

这是递归的一个很好的候选!不需要添加一个类别到UIView。

用法(来自你的视图控制器):

UIView *firstResponder = [self findFirstResponder:[self view]];

代码:

// This is a recursive function
- (UIView *)findFirstResponder:(UIView *)view {

    if ([view isFirstResponder]) return view; // Base case

    for (UIView *subView in [view subviews]) {
        if ([self findFirstResponder:subView]) return subView; // Recursion
    }
    return nil;
}

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

更新:我错了。你确实可以使用UIApplication.shared.sendAction(_:to:from:for:)来调用这个链接中演示的第一个响应器:http://stackoverflow.com/a/14135456/746890。


如果当前的第一响应器不在视图层次结构中,那么这里的大多数答案都不能真正找到它。例如,AppDelegate或UIViewController子类。

有一种方法可以保证你找到它,即使第一个responder对象不是UIView。

首先让我们实现一个相反的版本,使用UIResponder的下一个属性:

extension UIResponder {
    var nextFirstResponder: UIResponder? {
        return isFirstResponder ? self : next?.nextFirstResponder
    }
}

有了这个computed属性,我们可以从下到上找到当前的第一响应器,即使它不是UIView。例如,从一个视图到管理它的UIViewController,如果视图控制器是第一响应器。

然而,我们仍然需要一个自顶向下的解决方案,一个单独的变量来获得当前的第一个响应器。

首先是视图层次结构:

extension UIView {
    var previousFirstResponder: UIResponder? {
        return nextFirstResponder ?? subviews.compactMap { $0.previousFirstResponder }.first
    }
}

这将反向搜索第一个响应器,如果它找不到它,它会告诉它的子视图做同样的事情(因为它的子视图的next不一定是它自己)。有了这个,我们可以从任何视图中找到它,包括UIWindow。

最后,我们可以建立这个:

extension UIResponder {
    static var first: UIResponder? {
        return UIApplication.shared.windows.compactMap({ $0.previousFirstResponder }).first
    }
}

所以当你想检索第一个响应器时,你可以调用:

let firstResponder = UIResponder.first

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

使用Swift和一个特定的UIView对象,这可能会有所帮助:

func findFirstResponder(inView view: UIView) -> UIView? {
    for subView in view.subviews as! [UIView] {
        if subView.isFirstResponder() {
            return subView
        }
        
        if let recursiveSubView = self.findFirstResponder(inView: subView) {
            return recursiveSubView
        }
    }
    
    return nil
}

只需要把它放在你的UIViewController中,然后像这样使用它:

let firstResponder = self.findFirstResponder(inView: self.view)

请注意,结果是一个可选值,所以如果在给定的视图子视图层次结构中没有找到firstResponder,它将为nil。