有没有办法让iOS查看哪个孩子有第一响应者身份?

Tom*_*ert 42 cocoa-touch uiresponder ios responder-chain

在Mac OS X中,您可以找到第一个响应者:

[[self window] firstResponder]
Run Code Online (Sandbox Code Playgroud)

在iOS中有什么办法吗?或者您是否需要枚举子控件isFirstResponder并向每个控件发送消息?

Jak*_*ger 234

我真的很喜欢VJK的解决方案,但正如MattDiPasquale所说,它似乎比必要的更复杂.所以我写了这个更简单的版本:

Objective-C的

#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
Run Code Online (Sandbox Code Playgroud)

斯威夫特4

import UIKit

extension UIResponder {

    private static weak var _currentFirstResponder: UIResponder?

    static var currentFirstResponder: UIResponder? {
        _currentFirstResponder = nil
        UIApplication.shared.sendAction(#selector(UIResponder.findFirstResponder(_:)), to: nil, from: nil, for: nil)

        return _currentFirstResponder
    }

    @objc func findFirstResponder(_ sender: Any) {
        UIResponder._currentFirstResponder = self
    }
}
Run Code Online (Sandbox Code Playgroud)

我也把它作为一种类方法,因为它似乎更有意义.您现在可以找到第一个响应者,如下所示:[UIResponder currentFirstResponder]


  • 这非常简单,做得很好. (15认同)
  • fwiw:查看控制器**可以**成为第一响应者.您需要做的就是从`-canBecomeFirstResponder`返回`YES`.这非常适用于总是在屏幕上或处理快捷方式的输入附件视图,即使没有聚焦视图等等.因此,使用`UIView*`会出错. (4认同)
  • 这是一个Swift实现:http://stackoverflow.com/a/27140764/385979 (3认同)
  • 至少在iOS9上不起作用.我将焦点放在`scrollview`中的`TextField`中.在我按照描述的方式检查`firstResponder`并将ScrollView作为第一个响应者.同时`[[[[UIApplication sharedApplication] windows] firstObject] valueForKey:@"firstResponder"]`给出TextField. (3认同)
  • instancetype很适合:) (2认同)
  • @Senseful如果只从主线程调用它,那么`+ currentFirstResponder`可重入是没有意义的.你真的不应该从后台线程调用它.UIKit不是线程安全的. (2认同)

VJK*_*VJK 26

我写了一个类别UIResponder来找到第一个响应者

@interface UIResponder (firstResponder)
- (id) currentFirstResponder;
@end
Run Code Online (Sandbox Code Playgroud)

#import <objc/runtime.h>
#import "UIResponder+firstResponder.h"

static char const * const aKey = "first";

@implementation UIResponder (firstResponder)

- (id) currentFirstResponder {
    [[UIApplication sharedApplication] sendAction:@selector(findFirstResponder:) to:nil from:self forEvent:nil];
    id obj = objc_getAssociatedObject (self, aKey);
    objc_setAssociatedObject (self, aKey, nil, OBJC_ASSOCIATION_ASSIGN);
    return obj;
}

- (void) setCurrentFirstResponder:(id) aResponder {
    objc_setAssociatedObject (self, aKey, aResponder, OBJC_ASSOCIATION_ASSIGN);
}

- (void) findFirstResponder:(id) sender {
    [sender setCurrentFirstResponder:self];
}

@end
Run Code Online (Sandbox Code Playgroud)

然后在任何来自a的类中,UIResponder你可以通过调用获得第一个响应者

UIResponder* aFirstResponder = [self currentFirstResponder];
Run Code Online (Sandbox Code Playgroud)

但请记住首先导入UIResponder类别接口文件!

这使用了文档化的API,因此应该没有应用商店拒绝问题.

  • 使用` - [UIApplication sendAction:to:from:forEvent:]`很整洁!:)但是,不是使用关联的对象来引用`firstResponder`,为什么不使用静态变量,因为一次只有一个`firstResponder`? (4认同)

Eva*_*ski 11

您需要遍历所有子控件并测试该isFirstResponder属性.当你遇到时TRUE,突破循环.

UIView *firstResponder;
for (UIView *view in self.view.subviews) //: caused error
{
    if (view.isFirstResponder)
    {
        firstResponder = view;
        break;
    }
}
Run Code Online (Sandbox Code Playgroud)

更好的解决方案

雅各布的回答.

  • 此代码段仅搜索视图的直接后代,但在嵌套更深时不会找到第一个响应者. (7认同)

Pra*_*les 11

如果您需要第一响应者,您可以要求它重新签署其状态,这是一种让任何人辞职的方法.UIView有一个方法可以遍历所有UIViews子视图,并询问任何第一个响应者辞职.

[[self view] endEditing:YES];
Run Code Online (Sandbox Code Playgroud)

这是Apple的UIView Docs链接 "此方法查看当前视图及其子视图层次结构,该文本字段是当前第一个响应者.如果找到一个,则要求该文本字段作为第一响应者辞职.参数设置为YES,文本字段永远不会被询问;它被强制辞职."

  • 我认为OP不想强迫它辞职第一响应者,只是为了找到第一响应者. (2认同)
  • 然而,这是人们发现该片段的好地方. (2认同)