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

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

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

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

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


当前回答

你也可以这样尝试:

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

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

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

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

其他回答

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

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

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

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

这是一个类别,允许您通过调用[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)

使用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。

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

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

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

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

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

而不是遍历视图集合寻找设置了isFirstResponder的视图,我也将消息发送给nil,但我存储了消息的接收器,以便我可以返回它并对它做任何我想做的事情。

此外,我将在调用本身的deferred语句中保存找到的responder的optional归零。这确保在调用结束时没有引用保留——即使是弱引用。

import UIKit

private var _foundFirstResponder: UIResponder? = nil

extension UIResponder {

    static var first:UIResponder? {

        // Sending an action to 'nil' implicitly sends it to the first responder
        // where we simply capture it and place it in the _foundFirstResponder variable.
        // As such, the variable will contain the current first responder (if any) immediately after this line executes
        UIApplication.shared.sendAction(#selector(UIResponder.storeFirstResponder(_:)), to: nil, from: nil, for: nil)

        // The following 'defer' statement runs *after* this getter returns,
        // thus releasing any strong reference held by the variable immediately thereafter
        defer {
            _foundFirstResponder = nil
        }

        // Return the found first-responder (if any) back to the caller
        return _foundFirstResponder
    }

    // Make sure to mark this with '@objc' since it has to be reachable as a selector for `sendAction`
    @objc func storeFirstResponder(_ sender: AnyObject) {

        // Capture the recipient of this message (self), which is the first responder
        _foundFirstResponder = self
    }
}

有了上面的,我可以通过简单地这样做辞去第一响应者…

UIResponder.first?.resignFirstResponder()

但由于我的API实际上交还了第一个responder是什么,我可以对它做任何我想做的事情。

下面是一个示例,它检查当前的第一个响应器是否是一个带有helpMessage属性设置的UITextField,如果是,则在控件旁边的帮助气泡中显示它。我们通过屏幕上的“Quick Help”按钮来调用它。

func showQuickHelp(){

    if let textField = UIResponder?.first as? UITextField,
       let helpMessage = textField.helpMessage {
    
        textField.showHelpBubble(with:helpMessage)
    }
}

对上述的支持在UITextField上的扩展中定义,就像这样…

extension UITextField {
    var helpMessage:String? { ... }
    func showHelpBubble(with message:String) { ... }
}

现在要支持这个功能,我们所要做的就是决定哪些文本字段有帮助消息,UI会为我们处理其余的事情。