而不是遍历视图集合寻找设置了isFirstResponder的视图,我也将消息发送给nil,但我存储了消息的接收器,以便我可以返回它并对它做任何我想做的事情。
此外,我将在调用本身的deferred语句中保存找到的responder的optional归零。这确保在调用结束时没有引用保留——即使是弱引用。
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
}
}
有了上面的,我可以通过简单地这样做辞去第一响应者…
UIResponder.first?.resignFirstResponder()
但由于我的API实际上交还了第一个responder是什么,我可以对它做任何我想做的事情。
下面是一个示例,它检查当前的第一个响应器是否是一个带有helpMessage属性设置的UITextField,如果是,则在控件旁边的帮助气泡中显示它。我们通过屏幕上的“Quick Help”按钮来调用它。
func showQuickHelp(){
if let textField = UIResponder?.first as? UITextField,
let helpMessage = textField.helpMessage {
textField.showHelpBubble(with:helpMessage)
}
}
对上述的支持在UITextField上的扩展中定义,就像这样…
extension UITextField {
var helpMessage:String? { ... }
func showHelpBubble(with message:String) { ... }
}
现在要支持这个功能,我们所要做的就是决定哪些文本字段有帮助消息,UI会为我们处理其余的事情。