Jus*_*ble 334 cocoa-touch objective-c first-responder
我在一周前提交了我的应用程序,今天收到了可怕的拒绝电子邮件.它告诉我我的应用程序无法被接受,因为我使用的是非公共API; 具体来说,它说,
应用程序中包含的非公共API是firstResponder.
现在,违规的API调用实际上是我在SO上找到的解决方案:
UIWindow *keyWindow = [[UIApplication sharedApplication] keyWindow];
UIView *firstResponder = [keyWindow performSelector:@selector(firstResponder)];
Run Code Online (Sandbox Code Playgroud)
如何在屏幕上显示当前的第一响应者?我正在寻找一种不会让我的应用被拒绝的方法.
cdy*_*n37 529
如果你的最终目的只是让第一响应者辞职,这应该有效: [self.view endEditing:YES]
Tho*_*ler 330
在我的一个应用程序中,我经常希望第一个响应者在用户点击背景时辞职.为此,我在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
Run Code Online (Sandbox Code Playgroud)
iOS 7+
- (id)findFirstResponder
{
if (self.isFirstResponder) {
return self;
}
for (UIView *subView in self.view.subviews) {
if ([subView isFirstResponder]) {
return subView;
}
}
return nil;
}
Run Code Online (Sandbox Code Playgroud)
迅速:
extension UIView {
var firstResponder: UIView? {
guard !isFirstResponder else { return self }
for subview in subviews {
if let firstResponder = subview.firstResponder {
return firstResponder
}
}
return nil
}
}
Run Code Online (Sandbox Code Playgroud)
Swift中的用法示例:
if let firstResponder = view.window?.firstResponder {
// do something with `firstResponder`
}
Run Code Online (Sandbox Code Playgroud)
nev*_*vyn 183
操纵第一响应者的常用方法是使用零目标操作.这是一种向响应者链发送任意消息(从第一个响应者开始),然后继续向下直到某人响应消息(已实现与选择器匹配的方法)的方式.
对于解除键盘的情况,无论哪个窗口或视图是第一响应者,这都是最有效的方式:
[[UIApplication sharedApplication] sendAction:@selector(resignFirstResponder) to:nil from:nil forEvent:nil];
Run Code Online (Sandbox Code Playgroud)
这应该比甚至更有效[self.view.window endEditing:YES]
.
(感谢BigZaphod提醒我这个概念)
Jak*_*ger 94
这是一个类别,允许您通过调用快速找到第一个响应者[UIResponder currentFirstResponder]
.只需将以下两个文件添加到项目中:
UIResponder + FirstResponder.h
#import <Cocoa/Cocoa.h>
@interface UIResponder (FirstResponder)
+(id)currentFirstResponder;
@end
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
这里的诀窍是向nil发送一个动作将它发送给第一个响应者.
(我最初在这里发表这个答案:https://stackoverflow.com/a/14135456/322427)
Cod*_*der 38
这是一个基于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
}
}
Run Code Online (Sandbox Code Playgroud)
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
}
}
Run Code Online (Sandbox Code Playgroud)
can*_*boy 30
它不漂亮,但是当我不知道响应者是什么时,我辞职第一个响应者的方式:
在IB中或以编程方式创建UITextField.把它隐藏起来.如果您在IB中创建它,请将其链接到您的代码.
然后,当您想要关闭键盘时,将响应者切换到不可见的文本字段,并立即将其重新签名:
[self.invisibleField becomeFirstResponder];
[self.invisibleField resignFirstResponder];
Run Code Online (Sandbox Code Playgroud)
Poc*_*chi 19
对于一个Swift 3和4版本的nevyn的答案:
UIApplication.shared.sendAction(#selector(UIView.resignFirstResponder), to: nil, from: nil, for: nil)
Run Code Online (Sandbox Code Playgroud)
Sen*_*ful 10
这是一个报告正确的第一响应者的解决方案(例如,许多其他解决方案不报告UIViewController
作为第一响应者),不需要在视图层次结构上循环,并且不使用私有API.
它利用Apple的方法sendAction:to:from: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
Run Code Online (Sandbox Code Playgroud)
第一个响应者可以是类UIResponder的任何实例,因此尽管有UIViews,还有其他类可能是第一个响应者.例如,UIViewController
也可能是第一响应者.
在这个要点中,您将找到一种递归方式,通过从应用程序窗口的rootViewController开始循环遍历控制器层次结构来获取第一个响应者.
您可以通过执行来检索第一个响应者
- (void)foo
{
// Get the first responder
id firstResponder = [UIResponder firstResponder];
// Do whatever you want
[firstResponder resignFirstResponder];
}
Run Code Online (Sandbox Code Playgroud)
但是,如果第一个响应者不是UIView或UIViewController的子类,则此方法将失败.
为了解决这个问题,我们可以通过创建一个类别来UIResponder
执行不同的方法,并执行一些魔法调整,以便能够构建此类的所有生存实例的数组.然后,为了获得第一个响应者,我们可以简单地迭代并询问每个对象是否-isFirstResponder
.
可以在另一个要点中找到这种方法.
希望能帮助到你.
使用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
}
Run Code Online (Sandbox Code Playgroud)
只需将其放入您的UIViewController
使用中就可以这样使用:
let firstResponder = self.findFirstResponder(inView: self.view)
Run Code Online (Sandbox Code Playgroud)
请注意,结果是一个Optional值,因此如果在给定的视图子视图中找不到firstResponser,它将为nil.
我也没有遍历视图集合来查找已isFirstResponder
设置的视图,而是向 发送一条消息nil
,但我存储消息的接收者,以便我可以返回它并对其执行任何我希望的操作。
defer
此外,我将在调用本身的语句中保存找到的响应者的选项清零。这可以确保在调用结束时不会保留任何引用(即使是较弱的引用)。
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
}
}
Run Code Online (Sandbox Code Playgroud)
有了上面的内容,我就可以通过简单地这样做来辞职第一响应者......
UIResponder.first?.resignFirstResponder()
Run Code Online (Sandbox Code Playgroud)
但由于我的 API 实际上交还了第一响应者,所以我可以用它做任何我想做的事情。
下面的示例检查当前第一响应者是否设置了UITextField
属性helpMessage
,如果是,则将其显示在控件旁边的帮助气泡中。我们通过屏幕上的“快速帮助”按钮调用它。
func showQuickHelp(){
if let textField = UIResponder?.first as? UITextField,
let helpMessage = textField.helpMessage {
textField.showHelpBubble(with:helpMessage)
}
}
Run Code Online (Sandbox Code Playgroud)
对上述内容的支持是在扩展中定义的,UITextField
就像这样......
extension UITextField {
var helpMessage:String? { ... }
func showHelpBubble(with message:String) { ... }
}
Run Code Online (Sandbox Code Playgroud)
现在为了支持这个功能,我们所要做的就是决定哪些文本字段有帮助消息,UI 会为我们处理剩下的事情。
小智 5
如果您只需要在用户点击背景区域时关闭键盘,为什么不添加手势识别器并使用它来发送[[self view] endEditing:YES]
消息?
您可以在 xib 或故事板文件中添加 Tap 手势识别器并将其连接到一个动作,
看起来像这样然后完成
- (IBAction)displayGestureForTapRecognizer:(UITapGestureRecognizer *)recognizer{
[[self view] endEditing:YES];
}
Run Code Online (Sandbox Code Playgroud)
只是这里的情况是令人敬畏的 Jakob Egger 方法的 Swift 版本:
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
}
}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
153967 次 |
最近记录: |