Text field in UIAlertController should not be the first responder

I have a UIAlertController with a text box, for example:

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 text box has focus and the keyboard appears. Is it possible to change this behavior so that the text field becomes the first responder when the user clicks on it? I do not want the keyboard to appear at the same time when the warning controller was presented.

+4
source share
2 answers

Here is a small weak solution that includes related objects and the swizzling method.

, UITextField , , . , , - - canBecomeFirstResponder, . , canBecomeFirstResponder defaultValue - , .

UITextField, swizzling :

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 -- 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 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
    }
}

:

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
    }
}

, :

:

alertController.view.endEditing(true)

.

+1

. canEdit NO, YES "present view controller". , canEdit, YES NO. :

@interface ViewController (){

    BOOL canEdit; 

}

-(void)viewDidLoad{

[super viewDidLoad];

__weak typeof(self) weakSelf = self;
mentorAlert = [UIAlertController alertControllerWithTitle:@"Magen David Students" message:@"Hi! Please select your mentor!" preferredStyle:UIAlertControllerStyleAlert];
            [mentorAlert addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {

                textField.placeholder = @"Mentor";
                textField.delegate = weakSelf;
                textField.tag = 200;
                textField.userInteractionEnabled = YES;


            }];

            [self presentViewController:mentorAlert animated:YES completion:^{

                canEdit = YES;


            }];



}

-(BOOL)textFieldShouldBeginEditing:(UITextField *)textField{
if(textField.tag == 200){

    if (!canEdit)
            return NO;
else
return YES;
}
}
+2

All Articles