我提出程序一个个多星期前,并得到了可怕的排斥电子邮件的今天。它告诉我,我应用程序不能接受的,因为我使用一个非公开API;具体而言,它说,

非公共API,包括在应用程序是者.

现在,犯罪API话实际上是一个解决方案我在这里找到这样:

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

我怎么当前的第一个响应在屏幕上?我在寻找一种方法不会得到我的程序被拒绝。

有帮助吗?

解决方案

在我的应用程序之一,我经常想,如果用户点击背景的第一响应辞职。为了这个目的,我写上的UIView的类别,这是我的一个UIWindow调用。

是基于以下的和应该返回第一响应。

@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 +

- (id)findFirstResponder
{
    if (self.isFirstResponder) {
        return self;
    }
    for (UIView *subView in self.view.subviews) {
        if ([subView isFirstResponder]) {
            return subView;
        }
    }
    return nil;
}

<强>夫特:

extension UIView {
    var firstResponder: UIView? {
        guard !isFirstResponder else { return self }

        for subview in subviews {
            if let firstResponder = subview.firstResponder {
                return firstResponder
            }
        }

        return nil
    }
}

<强>在夫特用例:

if let firstResponder = view.window?.firstResponder {
    // do something with `firstResponder`
}

其他提示

如果你的最终目的是刚辞职的第一响应者,这应该工作:[self.view endEditing:YES]

操纵第一响应者的常见方式是将零目标动作中使用。这是发送的任意消息发送到响应者链(与第一应答器开始),并继续向下链的方式,直到有人响应该消息(已执行选择匹配的方法)。

有关驳回键盘的情况下,这是,将工作无论哪个窗口或视图中的最有效的方法是第一响应:

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

此应该比甚至[self.view.window endEditing:YES]更有效。

(感谢 BigZaphod 以提醒概念的我)

下面是一个类别,使您可以通过调用[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

这里的诀窍在于,发送到零的动作将其发送到所述第一响应。

(我最初发布这个答案在这里: https://stackoverflow.com/a/14135456/322427

下面是斯威夫特实施扩展基于雅各布·埃格的最优秀的答案:

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
    }
}

夫特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
    }
}

这不是很漂亮,但我辞职firstResponder时,我不知道的方式,响应者是什么:

创建的UITextField,无论是在IB或编程。隐藏它。其链接到你的代码,如果你在IB做到了。

然后,当你想放弃的键盘,切换响应,以无形的文本字段,并立即辞职吧:

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

有关的夫特3&4版 nevyn的 回答:

UIApplication.shared.sendAction(#selector(UIView.resignFirstResponder), to: nil, from: nil, for: nil)

这是一个报告正确的第一响应者的解决方案(许多其他解决方案不会报告 UIViewController 例如,作为第一响应者)不需要在视图层次结构上循环,并且不使用私有 API。

它利用了苹果的方法 发送操作:至:来自:forEvent:, ,它已经知道如何访问第一响应者。

我们只需要通过两种方式调整它:

  • 延长 UIResponder 所以它可以在第一响应者上执行我们自己的代码。
  • 子类 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

在第一响应可以是类UIResponder的任何实例,所以有其他类可能是尽管UIViews第一响应者。例如UIViewController也可能是第一个响应者。

这个要点,你会发现一个递归的方式通过的循环,以获得第一响应从应用程序的窗口的开始RootViewController的控制器的层次结构。

您可以通过执行检索,则第一响应

- (void)foo
{
    // Get the first responder
    id firstResponder = [UIResponder firstResponder];

    // Do whatever you want
    [firstResponder resignFirstResponder];      
}

然而,如果第一反应者没有的UIView或UIViewController中的一个子类,这种方法将失败。

要解决这个问题,我们可以通过创建UIResponder类别做了不同的方法,并执行一些魔术swizzeling能够建立这个类的所有生活实例的数组。然后,让我们可以在第一响应简单的迭代,并要求每个对象,如果-isFirstResponder

这个方法可以在其他要旨

希望它能帮助。

使用的夫特并用的特定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)

请注意该结果是因此这将是零的情况下,没有被firstResponser在给定的视图子视图中发现的<强>可选值

遍历这可能是第一响应,并使用- (BOOL)isFirstResponder以确定它们是否是当前的看法。

彼得·斯坦伯格只是啾啾了解民营通知UIWindowFirstResponderDidChangeNotification,而如果你愿意,你可以观察观看firstResponder变化。

如果你只需要当用户点击一个背景区,以杀死键盘为什么不添加一个手势识别,并用它来发送[[self view] endEditing:YES]消息?

可以在XIB或情节串连图板文件中添加敲击手势识别器,并将其连接到一个动作,

看起来是这样的接完

- (IBAction)displayGestureForTapRecognizer:(UITapGestureRecognizer *)recognizer{
     [[self view] endEditing:YES];
}

只要将它的情况下这里是真棒雅各布艾格的做法斯威夫特版本:

import UIKit

private weak var currentFirstResponder: UIResponder?

extension UIResponder {

    static func firstResponder() -> UIResponder? {
        currentFirstResponder = nil
        UIApplication.sharedApplication().sendAction(#selector(self.findFirstResponder(_:)), to: nil, from: nil, forEvent: nil)
        return currentFirstResponder
    }

    func findFirstResponder(sender: AnyObject) {
        currentFirstResponder = self
    }

}

这是我做了什么,以查找的UITextField是firstResponder当用户点击保存/在ModalViewController取消:

    NSArray *subviews = [self.tableView subviews];

for (id cell in subviews ) 
{
    if ([cell isKindOfClass:[UITableViewCell class]]) 
    {
        UITableViewCell *aCell = cell;
        NSArray *cellContentViews = [[aCell contentView] subviews];
        for (id textField in cellContentViews) 
        {
            if ([textField isKindOfClass:[UITextField class]]) 
            {
                UITextField *theTextField = textField;
                if ([theTextField isFirstResponder]) {
                    [theTextField resignFirstResponder];
                }

            }
        }

    }

}

这是我在我的UIViewController类。有用的很多东西,包括获得第一个响应。块是伟大的!

- (UIView*) enumerateAllSubviewsOf: (UIView*) aView UsingBlock: (BOOL (^)( UIView* aView )) aBlock {

 for ( UIView* aSubView in aView.subviews ) {
  if( aBlock( aSubView )) {
   return aSubView;
  } else if( ! [ aSubView isKindOfClass: [ UIControl class ]] ){
   UIView* result = [ self enumerateAllSubviewsOf: aSubView UsingBlock: aBlock ];

   if( result != nil ) {
    return result;
   }
  }
 }    

 return nil;
}

- (UIView*) enumerateAllSubviewsUsingBlock: (BOOL (^)( UIView* aView )) aBlock {
 return [ self enumerateAllSubviewsOf: self.view UsingBlock: aBlock ];
}

- (UIView*) findFirstResponder {
 return [ self enumerateAllSubviewsUsingBlock:^BOOL(UIView *aView) {
  if( [ aView isFirstResponder ] ) {
   return YES;
  }

  return NO;
 }];
}

与一个类别 UIResponder, 它是可能的法律要求的 UIApplication 目来告诉你是谁的第一个响应。

看看这个:

是否有任何方法的问iOS查看其儿童的第一响应者的地位?

您可以选择以下UIView扩展得到它的(由丹尼尔·信用)的:

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

您也可以尝试这样的:

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

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

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

我没有尝试,但它似乎是一个不错的解决方案。

这罗密欧 https://stackoverflow.com/a/2799675/661022 该解决方案是凉,但我注意到该代码需要多一个循环。我与tableViewController工作。 我编辑的脚本,然后我检查。一切工作完美。

我推荐尝试这样:

- (void)findFirstResponder
{
    NSArray *subviews = [self.tableView subviews];
    for (id subv in subviews )
    {
        for (id cell in [subv subviews] ) {
            if ([cell isKindOfClass:[UITableViewCell class]])
            {
                UITableViewCell *aCell = cell;
                NSArray *cellContentViews = [[aCell contentView] subviews];
                for (id textField in cellContentViews)
                {
                    if ([textField isKindOfClass:[UITextField class]])
                    {
                        UITextField *theTextField = textField;
                        if ([theTextField isFirstResponder]) {
                            NSLog(@"current textField: %@", theTextField);
                            NSLog(@"current textFields's superview: %@", [theTextField superview]);
                        }
                    }
                }
            }
        }
    }
}

这是很好的候选人递归!没有必要一个类别添加到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;
}

可以调用API privite这样,苹果忽略:

UIWindow *keyWindow = [[UIApplication sharedApplication] keyWindow];
SEL sel = NSSelectorFromString(@"firstResponder");
UIView   *firstResponder = [keyWindow performSelector:sel];

我想与你共享我的执行情况发现的UIView的任何地方第一个响应。我希望它可以帮助和我的英语很抱歉。感谢

+ (UIView *) findFirstResponder:(UIView *) _view {

UIView *retorno;

for (id subView in _view.subviews) {

    if ([subView isFirstResponder])
        return subView;

    if ([subView isKindOfClass:[UIView class]]) {
        UIView *v = subView;

        if ([v.subviews count] > 0) {
            retorno = [self findFirstResponder:v];
            if ([retorno isFirstResponder]) {
                return retorno;
            }
        }
    }
}

return retorno;

}

@thomas -müller的响应

extension UIView {

    func firstResponder() -> UIView? {
        if self.isFirstResponder() {
            return self
        }

        for subview in self.subviews {
            if let firstResponder = subview.firstResponder() {
                return firstResponder
            }
        }

        return nil
    }

}

我比大多数这里稍微不同的方法。而不是通过视图寻找有isFirstResponder组的一个集合迭代,我也将消息发送给nil,但我保存接收器(如果有的话),然后返回一个值,使主叫方获得实际的实例(同样,如果有)。

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 for return
        UIApplication.shared.sendAction(#selector(UIResponder.storeFirstResponder(_:)), to: nil, from: nil, for: nil)

        // The following 'defer' statement executes after the return
        // While I could use a weak ref and eliminate this, I prefer a
        // hard ref which I explicitly clear as it's better for race conditions
        defer {
            _foundFirstResponder = nil
        }

        return _foundFirstResponder
    }

    @objc func storeFirstResponder(_ sender: AnyObject) {
        _foundFirstResponder = self
    }
}

然后我可以辞职第一响应者,如果有的话,这样做...

return UIResponder.first?.resignFirstResponder()

不过,我现在也可以做到这一点...

if let firstResponderTextField = UIResponder?.first as? UITextField {
    // bla
}

下面工作代码。

- (id)ht_findFirstResponder
{
    //ignore hit test fail view
    if (self.userInteractionEnabled == NO || self.alpha <= 0.01 || self.hidden == YES) {
        return nil;
    }
    if ([self isKindOfClass:[UIControl class]] && [(UIControl *)self isEnabled] == NO) {
        return nil;
    }

    //ignore bound out screen
    if (CGRectIntersectsRect(self.frame, [UIApplication sharedApplication].keyWindow.bounds) == NO) {
        return nil;
    }

    if ([self isFirstResponder]) {
        return self;
    }

    for (UIView *subView in self.subviews) {
        id result = [subView ht_findFirstResponder];
        if (result) {
            return result;
        }
    }
    return nil;
}   

更新:我错了。事实上,你可以使用UIApplication.shared.sendAction(_:to:from:for:)调用这个环节展示了第一个响应者: http://stackoverflow.com/a/14135456/746890


这里大部分的答案真的不能找到当前第一响应者,如果它不在视图层次。例如,AppDelegateUIViewController子类。

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

首先让实现它的一个反转版本,使用nextUIResponder属性:

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

通过这个计算的属性,我们可以从底部找到当前第一响应者顶部,即使它不是UIView。例如,从viewUIViewController谁在管理它,如果视图控制器是第一响应。

但是,我们仍然需要一个自上而下的分辨率,单var来获得当前的第一个响应者。

首先与视图层次结构:

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
scroll top