objective-c 在不使用私有 API 的情况下获取当前的第一响应者
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1823317/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Get the current first responder without using a private API
提问by Justin Kredible
I submitted my app a little over a week ago and got the dreaded rejection email today. It tells me that my app cannot be accepted because I'm using a non-public API; specifically, it says,
我在一个多星期前提交了我的应用程序,今天收到了可怕的拒绝电子邮件。它告诉我我的应用程序无法被接受,因为我使用的是非公共 API;具体来说,它说,
The non-public API that is included in your application is firstResponder.
您的应用程序中包含的非公共 API 是 firstResponder。
Now, the offending API call is actually a solution I found here on SO:
现在,有问题的 API 调用实际上是我在 SO 上找到的解决方案:
UIWindow *keyWindow = [[UIApplication sharedApplication] keyWindow];
UIView *firstResponder = [keyWindow performSelector:@selector(firstResponder)];
How do I get the current first responder on the screen? I'm looking for a way that won't get my app rejected.
如何在屏幕上显示当前的第一响应者?我正在寻找一种不会让我的应用程序被拒绝的方法。
采纳答案by Thomas Müller
In one of my applications I often want the first responder to resign if the user taps on the background. For this purpose I wrote a category on UIView, which I call on the UIWindow.
在我的一个应用程序中,如果用户点击背景,我经常希望第一响应者辞职。为此,我在 UIView 上写了一个类别,我在 UIWindow 上调用它。
The following is based on that and should return the first responder.
以下内容基于此,应返回第一响应者。
@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+
iOS 7+
- (id)findFirstResponder
{
if (self.isFirstResponder) {
return self;
}
for (UIView *subView in self.view.subviews) {
if ([subView isFirstResponder]) {
return subView;
}
}
return nil;
}
Swift:
迅速:
extension UIView {
var firstResponder: UIView? {
guard !isFirstResponder else { return self }
for subview in subviews {
if let firstResponder = subview.firstResponder {
return firstResponder
}
}
return nil
}
}
Usage example in Swift:
Swift 中的用法示例:
if let firstResponder = view.window?.firstResponder {
// do something with `firstResponder`
}
回答by cdyson37
If your ultimate aim is just to resign the first responder, this should work: [self.view endEditing:YES]
如果您的最终目标只是让第一响应者辞职,这应该可行: [self.view endEditing:YES]
回答by nevyn
A common way of manipulating the first responder is to use nil targeted actions. This is a way of sending an arbitrary message to the responder chain (starting with the first responder), and continuing down the chain until someone responds to the message (has implemented a method matching the selector).
操纵第一响应者的常见方法是使用 nil 目标操作。这是一种将任意消息发送到响应者链(从第一个响应者开始)并继续沿链向下直到有人响应该消息(已实现与选择器匹配的方法)的方式。
For the case of dismissing the keyboard, this is the most effective way that will work no matter which window or view is first responder:
对于关闭键盘的情况,无论哪个窗口或视图是第一响应者,这是最有效的方法:
[[UIApplication sharedApplication] sendAction:@selector(resignFirstResponder) to:nil from:nil forEvent:nil];
This should be more effective than even [self.view.window endEditing:YES].
这应该比甚至更有效[self.view.window endEditing:YES]。
(Thanks to BigZaphodfor reminding me of the concept)
(感谢BigZaphod提醒我这个概念)
回答by Jakob Egger
Here's a category that allows you to quickly find the first responder by calling [UIResponder currentFirstResponder]. Just add the following two files to your project:
这是一个类别,可让您通过调用 快速找到第一响应者[UIResponder currentFirstResponder]。只需将以下两个文件添加到您的项目中:
UIResponder+FirstResponder.h
UIResponder+FirstResponder.h
#import <Cocoa/Cocoa.h>
@interface UIResponder (FirstResponder)
+(id)currentFirstResponder;
@end
UIResponder+FirstResponder.m
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
The trick here is that sending an action to nil sends it to the first responder.
这里的技巧是将一个动作发送到 nil 将它发送给第一响应者。
(I originally published this answer here: https://stackoverflow.com/a/14135456/322427)
(我最初在这里发布了这个答案:https: //stackoverflow.com/a/14135456/322427)
回答by Code Commander
Here is a Extension implemented in Swift based on Jakob Egger's most excellent answer:
这是基于 Jakob Egger 最优秀答案在 Swift 中实现的扩展:
import UIKit
extension UIResponder {
// Swift 1.2 finally supports static vars!. If you use 1.1 see:
// http://stackoverflow.com/a/24924535/385979
private weak static var _currentFirstResponder: UIResponder? = nil
public class func currentFirstResponder() -> UIResponder? {
UIResponder._currentFirstResponder = nil
UIApplication.sharedApplication().sendAction("findFirstResponder:", to: nil, from: nil, forEvent: nil)
return UIResponder._currentFirstResponder
}
internal func findFirstResponder(sender: AnyObject) {
UIResponder._currentFirstResponder = self
}
}
Swift 4
斯威夫特 4
import UIKit
extension UIResponder {
private weak static var _currentFirstResponder: UIResponder? = nil
public static var current: UIResponder? {
UIResponder._currentFirstResponder = nil
UIApplication.shared.sendAction(#selector(findFirstResponder(sender:)), to: nil, from: nil, for: nil)
return UIResponder._currentFirstResponder
}
@objc internal func findFirstResponder(sender: AnyObject) {
UIResponder._currentFirstResponder = self
}
}
回答by cannyboy
It's not pretty, but the way I resign the firstResponder when I don't know what that the responder is:
这不是很漂亮,但是当我不知道响应者是什么时,我退出 firstResponder 的方式:
Create an UITextField, either in IB or programmatically. Make it Hidden. Link it up to your code if you made it in IB.
在 IB 中或以编程方式创建一个 UITextField。使其隐藏。如果您是在 IB 中制作的,则将其链接到您的代码。
Then, when you want to dismiss the keyboard, you switch the responder to the invisible text field, and immediately resign it:
然后,当你想关闭键盘时,你将响应者切换到不可见的文本字段,并立即辞职:
[self.invisibleField becomeFirstResponder];
[self.invisibleField resignFirstResponder];
回答by Pochi
回答by Senseful
Here's a solution which reports the correct first responder (many other solutions won't report a UIViewControlleras the first responder, for example), doesn't require looping over the view hierarchy, and doesn't use private APIs.
这是一个报告正确的第一响应者的解决方案(例如,许多其他解决方案不会将 a 报告UIViewController为第一响应者),不需要遍历视图层次结构,并且不使用私有 API。
It leverages Apple's method sendAction:to:from:forEvent:, which already knows how to access the first responder.
它利用了 Apple 的方法sendAction:to:from:forEvent:,该方法已经知道如何访问第一响应者。
We just need to tweak it in 2 ways:
我们只需要通过两种方式调整它:
- Extend
UIResponderso it can execute our own code on the first responder. - Subclass
UIEventin order to return the first responder.
- 扩展
UIResponder以便它可以在第一响应者上执行我们自己的代码。 - 子类
UIEvent以返回第一响应者。
Here is the code:
这是代码:
@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
回答by Jeehut
Using Swiftand with a specific UIViewobjectthis might help:
使用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
}
Just place it in your UIViewControllerand use it like this:
只需将它放入您的UIViewController并像这样使用它:
let firstResponder = self.findFirstResponder(inView: self.view)
Take note that the result is an Optional valueso it will be nil in case no firstResponser was found in the given views subviews.
请注意,结果是一个Optional 值,因此如果在给定的视图子视图中没有找到 firstResponser,它将为零。
回答by vilanovi
The first responder can be any instance of the class UIResponder, so there are other classes that might be the first responder despite the UIViews. For example UIViewControllermight also be the first responder.
第一响应者可以是 UIResponder 类的任何实例,因此尽管有 UIViews,还有其他类可能是第一响应者。例如,UIViewController也可能是第一响应者。
In this gistyou will find a recursive way to get the first responder by looping through the hierarchy of controllers starting from the rootViewController of the application's windows.
在这个要点中,您将找到一种递归方式,通过从应用程序窗口的 rootViewController 开始循环遍历控制器的层次结构来获得第一响应者。
You can retrieve then the first responder by doing
您可以通过执行以下操作来检索第一响应者
- (void)foo
{
// Get the first responder
id firstResponder = [UIResponder firstResponder];
// Do whatever you want
[firstResponder resignFirstResponder];
}
However, if the first responder is not a subclass of UIView or UIViewController, this approach will fail.
但是,如果第一响应者不是 UIView 或 UIViewController 的子类,则此方法将失败。
To fix this problem we can do a different approach by creating a category on UIResponderand perform some magic swizzeling to be able to build an array of all living instances of this class. Then, to get the first responder we can simple iterate and ask each object if -isFirstResponder.
为了解决这个问题,我们可以采取不同的方法,通过创建一个类别UIResponder并执行一些魔术调配来构建一个包含此类的所有活动实例的数组。然后,为了获得第一响应者,我们可以简单地迭代并询问每个对象是否-isFirstResponder。
This approach can be found implemented in this other gist.
这种方法可以在这个其他要点中找到。
Hope it helps.
希望能帮助到你。

