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