Get the current first responder without using a private API

前端 未结 28 2521
夕颜
夕颜 2020-11-22 05:03

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; specif

28条回答
  •  佛祖请我去吃肉
    2020-11-22 05:41

    I have a slightly different approach than most. Rather than iterate through the collection of views looking for the one that has isFirstResponder set, I too send a message to nil, but I store the receiver of the message so I can return it and do whatever I wish with it.

    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
        }
    
        @objc func storeFirstResponder(_ sender: AnyObject) {
    
            // Capture the recipient of this message (self), which is the first responder
            _foundFirstResponder = self
        }
    }
    

    With the above, I can resign the first responder by simply doing this...

    UIResponder.first?.resignFirstResponder()
    

    But since my API actually hands back whatever the first responder is, I can do whatever I want with it.

    Here's an example that checks if the current first responder is a UITextField with a helpMessage property set, and if so, shows it in a help bubble right next to the control. We call this from a 'Quick Help' button on our screen.

    func showQuickHelp(){
    
        if let textField   = UIResponder?.first as? UITextField,
           let helpMessage = textField.helpMessage {
        
            textField.showHelpBubble(with:helpMessage)
        }
    }
    

    The support for the above is defined in an extension on UITextField like so...

    extension UITextField {
        var helpMessage:String? { ... }
        func showHelpBubble(with message:String) { ... }
    }
    

    Now to support this feature, all we have to do is decide which text fields have help messages and the UI takes care of the rest for us.

提交回复
热议问题