本文介绍了UIAlertController中的文本字段不应是第一响应者的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个带有文本字段的UIAlertController,如下所示:

I have an UIAlertController with a textfield in it, like this:

let alertController = UIAlertController(title: "Title", message: "Hello, World!", preferredStyle: .Alert)

    let someAction = UIAlertAction(title: "Action", style: .Default) { (_) in }
    let cancelAction = UIAlertAction(title: "Cancel", style: .Cancel) {(_) in }

    alertController.addAction(someAction)
    alertController.addAction(cancelAction)
    alertController.addTextFieldWithConfigurationHandler { textfield in
        textfield.text = "Text"
    }

    self.presentViewController(alertController, animated: true, completion: nil)

在显示控制器时,文本字段具有焦点并且键盘会抬起。是否可以更改该行为,以使文本字段仅在用户点击时成为第一响应者?我不希望在显示警报控制器的同时显示键盘。

When the controller is presented, the textfield has focus and the keyboard comes up. Is it possible to change that behavior so that the textfield only becomes first responder when the user taps on it? I don't want the keyboard to be presented at the same time the alert controller is presented.

推荐答案

这是一个稍微有些棘手的解决方案

Here's one slightly hacky solution that involves associated objects and method swizzling.

这个想法是给 UITextField 一个闭包,使外部类有发言权文本字段是否可以成为第一响应者。此闭包将返回一个布尔值,该值指示文本字段是由于用户交互还是由于在文本字段上以编程方式调用了 canBecomeFirstResponder 而试图成为第一响应者。它还会传回 canBecomeFirstResponder 通常会在 defaultValue 参数中返回的值-在此不需要

The idea is to give UITextField a closure that gives outside classes a say in whether a text field can become the first responder or not. This closure passes back boolean value indicating whether the text field is trying to become the first responder due to user interaction or due to canBecomeFirstResponder having been programmatically called on the text field. It also passes back the value that canBecomeFirstResponder would return normally in the defaultValue parameter -- this isn't needed in this case, but as a general solution it could be useful to have.

首先,我们添加 UITextField 扩展名,

First, we add the UITextField extension that will handle the swizzling and associated object stuff:

public extension UITextField {

    // Private struct to hold our associated object key
    private struct AssociatedKeys {
        static var HandlerKey = "xxx_canBecomeFirstResponder"
    }

    // Typealias for the type of our associated object closure
    public typealias CanBecomeFirstResponderHandler = (fromUserInteraction: Bool,
        defaultValue: Bool) -> Bool

    // We need this private class to wrap the closure in an object
    // because objc_setAssociatedObject takes an 'AnyObject', but
    // closures are not 'AnyObject's -- they are instead 'Any's
    private class AnyValueWrapper {
        var value: Any?
    }

    // Define the closure as a computed property and use associated objects to
    // store/retrieve it.
    public var canBecomeFirstResponderHandler: CanBecomeFirstResponderHandler? {
        get {
            // Get the AnyValueWrapper object
            let wrapper = objc_getAssociatedObject(self,
                                                   &AssociatedKeys.HandlerKey)

            // ...then get the closure from its `value` property
            return (wrapper as? AnyValueWrapper)?.value
                as? CanBecomeFirstResponderHandler
        }

        set {
            // If the new value is not nil:
            if let newValue = newValue {

                // Create a new AnyValueWrapper and set its `value` property to 
                // the new closure
                let wrapper = AnyValueWrapper()
                wrapper.value = newValue

                // Set this wrapper object as an associated object
                objc_setAssociatedObject(
                    self,
                    &AssociatedKeys.HandlerKey,
                    wrapper,
                    .OBJC_ASSOCIATION_RETAIN_NONATOMIC
                )

                return
            }

            // If the new value is nil, remove any existing associated object for
            // the closure
            objc_setAssociatedObject(
                self,
                &AssociatedKeys.HandlerKey,
                nil,
                .OBJC_ASSOCIATION_RETAIN_NONATOMIC
            )
        }
    }

    // Set up the method swizzling when the `UITextField` class is initialized
    public override class func initialize() {
        struct Static {
            static var token: dispatch_once_t = 0
        }

        // Make sure we are not in a subclass when this method is called
        if self !== UITextField.self {
            return
        }

        // Swizzle the canBecomeFirstResponder method.
        dispatch_once(&Static.token) {
            let originalSelector =
                #selector(UITextField.canBecomeFirstResponder)
            let swizzledSelector =
                #selector(UITextField.xxx_canBecomeFirstResponder)

            let originalMethod = class_getInstanceMethod(self, originalSelector)
            let swizzledMethod = class_getInstanceMethod(self, swizzledSelector)

            let didAddMethod =
                class_addMethod(self,
                                originalSelector,
                                method_getImplementation(swizzledMethod),
                                method_getTypeEncoding(swizzledMethod))

            if didAddMethod {
                class_replaceMethod(self, swizzledSelector,
                                    method_getImplementation(originalMethod),
                                    method_getTypeEncoding(originalMethod))
            }
            else {
                method_exchangeImplementations(originalMethod, swizzledMethod)
            }
        }
    }

    // MARK: - Method Swizzling

    // Our swizzled method that replaces the canBecomeFirstResponder 
    // method of `UITextField`
    func xxx_canBecomeFirstResponder() -> Bool {

        // Get the default value of canBecomeFirstResponder
        let defaultValue = xxx_canBecomeFirstResponder()

        // If we have a closure in our associated object:
        if let canBecomeFirstResponder = canBecomeFirstResponderHandler {

            // Determine if the user interacted with the text field and set
            // a flag if so. We do this by checking all gesture recognizers
            // of the text field to see if any of them have begun, changed, or
            // ended at the time of calling `canBecomeFirstResponder`.
            // It's reasonable to assume that if `canBecomeFirstResponder` is
            // called when any of these conditions are true, then the text field
            // must be trying to become the first responder due to a user
            // interaction.
            var isFromUserInteraction = false
            if let gestureRecognizers = gestureRecognizers {
                for gestureRecognizer in gestureRecognizers {
                    if (gestureRecognizer.state == .Began ||
                        gestureRecognizer.state == .Changed ||
                        gestureRecognizer.state == .Ended)
                    {
                        isFromUserInteraction = true
                        break
                    }
                }
            }

            // Call our closure and pass in the two boolean values,
            // then return the result
            return canBecomeFirstResponder(
                fromUserInteraction: isFromUserInteraction,
                defaultValue: defaultValue
            )
        }

        // If we don't have a closure in our associated object,
        // just return the original value
        return defaultValue
    }
}

然后,您可以在警报控制器文本字段配置处理程序中使用此闭包,如下所示:

Then, you can use this closure in your alert controller text field configuration handler like so:

alertController.addTextFieldWithConfigurationHandler { textfield in
    textfield.text = "Text"

    // Set the closure on the text field. You can use the passed in flags if you
    // want or you can simply return fromUserInteraction to only allow user
    // interaction to let the text field become the first responder, as is done
    // here:
    textfield.canBecomeFirstResponderHandler = {
        fromUserInteraction, defaultValue in
        return fromUserInteraction
    }
}






不再有效的旧解决方案:



添加


Old solution that no longer works:

Adding this:

alertController.view.endEditing(true)

在出现警报控制器之前,警报控制器将从所有文本字段中退出第一响应者,并防止在出现提示时出现键盘。

right before presenting the alert controller will resign first responder from all text fields and prevent the keyboard from appearing while presenting.

这篇关于UIAlertController中的文本字段不应是第一响应者的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-18 15:53