Search code examples
iosswiftuitextfieldibactionuitextfielddelegate

Initiate IBAction of a UIButton on Return key


I have a SignUp Form which includes a number of UITextfields and UIButton. I have set up UITextfield delegates so that the when user starts editing on a UITextfield and presses the return key it goes to the next UITextfield.

SignUp Form

DoB, ISD Code, Gender and Nationality are UIButtons which have IBActions, and rest of them are UITextfields

Is it possible to initiate a IBAction of a UIButton on pressing the return key from a Textfield. So that the user can sequentially add the necessary data to the signup form.

What i have done so far ..

    func textFieldShouldReturn(_ textField: UITextField) -> Bool{

    if let nextField = textField.superview?.viewWithTag(textField.tag + 1) as? UITextField
    {
        nextField.becomeFirstResponder()
    }
    else {
        // Not found, so remove keyboard.
        signupScrollView .setContentOffset(CGPoint( x: 0, y: 0), animated: true)
        textField.resignFirstResponder()
        return true
    }
    // Do not add a line break
    return false
}

func textFieldDidBeginEditing(_ textField: UITextField) {
    signupScrollView .setContentOffset(CGPoint( x: 0, y: textField.center.y-200), animated: true)

}

Solution

  • Assuming your textFields and buttons have a common sequence of tags, you can basically do something like the following:

    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
    
        let nextView = textField.superview?.viewWithTag(textField.tag + 1)
    
        if let nextTextField = nextView as? UITextField {
            //next view is a textField so make next textField first responder
            nextTextField.becomeFirstResponder()
        }
        //just this extra case is required
        else if let nextButton = nextView as? UIButton {
            //next view is a button so call it's associated action
            //(assuming your button's @IBAction is on the Touch Up Inside event)
            nextButton.sendActions(for: .touchUpInside)
    
            /*
             a button's action will be performed so if you want to 
             perform the return action on the textField then either
             return true/false. Decide that as per your requirement
             */
            return true
        }
        else {
            //...
        }
        
        //...
    }
    

    The above logic is enough to answer your question but works only when the user is on a textField and has tapped on the keyboard's Return button.

    Now... the thing is that on the end of a button actions, if you want to continue to the next view; textField or button, you can either1 explicitly code to make the next view active.

    Example:

    • After ISD Code is done you could make the mobile textField as first responder
    • After Gender is done, you could call the button action for Nationality

    Or2...

    We can modify the solution to handle textField as well as buttons alike with a common helper function, that we will call goNext(from:) to be implemented in textFieldShouldReturn(_:) as well as after the button is to complete it's intended logic flow.

    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        let nextView = goNext(from: textField)
        
        if let nextTextField = nextView as? UITextField {
            //Next field was a textField so keyboard should stay
            return false
        }
        
        textField.resignFirstResponder()
        return true
    }
    
    @discardableResult func goNext(from sender: UIView) -> UIView? {
        let nextView = sender.superview?.viewWithTag(sender.tag + 1)
        print(nextView?.tag ?? "No view with tag \(sender.tag + 1)")
        
        if let nextTextField = nextView as? UITextField {
            nextTextField.becomeFirstResponder()
        }
        else if let nextButton = nextView as? UIButton {
            nextButton.sendActions(for: .touchUpInside)
        }
        else {
            print("Done")
            signupScrollView.setContentOffset(CGPoint(x: 0, y: 0), 
                                              animated: true)
            sender.resignFirstResponder()
        }
    
        return nextView
    }
    

    Now for the button part, you need to perform goNext(from: button) whenever the associated button has completed it's logic.

    Example:

    • User has successfully selected Date of Birth: You should then be calling

      goNext(from: dobButton)