Next/Done button using Swift with textFieldShouldReturn
Asked Answered
P

4

33

I have a MainView that adds a subview (signUpWindow) when a sign up button is pressed.

In my signUpWindow subview (SignUpWindowView.swift), I set up each field with a function, as an example:

func confirmPasswordText()
    {
        confirmPasswordTextField.frame=CGRectMake(50, 210, 410, 50)
        confirmPasswordTextField.placeholder=("Confirm Password")
        confirmPasswordTextField.textColor=textFieldFontColor
        confirmPasswordTextField.secureTextEntry=true
        confirmPasswordTextField.returnKeyType = .Next
        confirmPasswordTextField.clearButtonMode = .WhileEditing
        confirmPasswordTextField.tag=5
        self.addSubview(confirmPasswordTextField)
    }

I have the keyboard moving the signUpWindow up and down when it appears and disappears in the MainView.

SignUpWindowView implements the UITextFieldDelegate

My problem is that I am trying to configure the Next/Done button on the keyboard and am not sure which view (MainView or SignUpWindowView) to add the textFieldShouldReturn function. I have tried both, but can't even get a println to fire to test to see if the function is even being executed. Once I get the textFieldShouldReturn to fire, I am confident I can execute the necessary code to get the Next/Done buttons to do what I want, and will post the final solution to include the Next/Done function.

UPDATED to include an abbreviated version of SignUpWindowView.swift

import UIKit

class SignUpWindowView: UIView,UITextFieldDelegate {

let firstNameTextField:UITextField=UITextField()
let lastNameTextField:UITextField=UITextField()

override func drawRect(rect: CGRect){
    func firstNameText(){
        firstNameTextField.delegate=self
        firstNameTextField.frame=CGRectMake(50, 25, 200, 50)
        firstNameTextField.placeholder="First Name"
        firstNameTextField.returnKeyType = .Next
        self.addSubview(firstNameTextField)
     }

    func lastNameText(){
        lastNameTextField.delegate=self
        lastNameTextField.frame=CGRectMake(260, 25, 200, 50)
        lastNameTextField.placeholder="Last Name"
        lastNameTextField.returnKeyType = .Done
        self.addSubview(lastNameTextField)
     }

    func textFieldShouldReturn(textField: UITextField!) -> Bool{
        println("next button should work")
        if (textField === firstNameTextField)
        {
            firstNameTextField.resignFirstResponder()
            lastNameTextField.becomeFirstResponder()
        }
        return true
     }

    firstNameText()
    lastNameText()
}
Plantain answered 28/4, 2015 at 17:15 Comment(2)
what do you set to be the textfields delegate?Lynching
confirmPasswordTextField.delegate=selfPlantain
P
13

I was attempting to test my textfields in the SignUpWindowView.swift, which is where all of the textFields are created. But, since I place SignUpWindowView into my MainViewController as a subview, all of my UITextField "handling" needed to be done in the MainView and NOT its subview.

So here is my entire code (at the moment) for my MainViewController, which handles moving my SignUpWindowView up/down when the keyboard is shown/hidden and then moves from one field to the next. When the user is in the last text field (whose keyboard Next button is now set to Done in the subview) the keyboard tucks away and the user can then submit the form with a signup button.

MainViewController:

import UIKit

@objc protocol ViewControllerDelegate
{
    func keyboardWillShowWithSize(size:CGSize, andDuration duration:NSTimeInterval)
    func keyboardWillHideWithSize(size:CGSize,andDuration duration:NSTimeInterval)
}

class ViewController: UIViewController,UITextFieldDelegate
{
    var keyboardDelegate:ViewControllerDelegate?

    let signUpWindow=SignUpWindowView()
    let signUpWindowPosition:CGPoint=CGPointMake(505, 285)

    override func viewDidLoad()
    {
        super.viewDidLoad()

        // Keyboard Notifications
        NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillShow:", name: UIKeyboardWillShowNotification, object: nil)
        NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillHide:", name: UIKeyboardWillHideNotification, object: nil)

        // set the textFieldDelegates
        signUpWindow.firstNameTextField.delegate=self
        signUpWindow.lastNameTextField.delegate=self
        signUpWindow.userNameTextField.delegate=self
        signUpWindow.passwordTextField.delegate=self
        signUpWindow.confirmPasswordTextField.delegate=self
        signUpWindow.emailTextField.delegate=self
    }


    func keyboardWillShow(notification: NSNotification)
    {
        var info:NSDictionary = notification.userInfo!
        let keyboardFrame = info[UIKeyboardFrameEndUserInfoKey] as! NSValue
        let keyboardSize = keyboardFrame.CGRectValue().size

        var keyboardHeight:CGFloat = keyboardSize.height

        let animationDurationValue = info[UIKeyboardAnimationDurationUserInfoKey] as! NSNumber

        var animationDuration : NSTimeInterval = animationDurationValue.doubleValue

        self.keyboardDelegate?.keyboardWillShowWithSize(keyboardSize, andDuration: animationDuration)

        // push up the signUpWindow
        UIView.animateWithDuration(animationDuration, delay: 0.25, options: UIViewAnimationOptions.CurveEaseInOut, animations: {
            self.signUpWindow.frame = CGRectMake(self.signUpWindowPosition.x, (self.signUpWindowPosition.y - keyboardHeight+140), self.signUpWindow.bounds.width, self.signUpWindow.bounds.height)
            }, completion: nil)
    }

    func keyboardWillHide(notification: NSNotification)
    {
        var info:NSDictionary = notification.userInfo!

        let keyboardFrame = info[UIKeyboardFrameEndUserInfoKey] as! NSValue
        let keyboardSize = keyboardFrame.CGRectValue().size

        var keyboardHeight:CGFloat = keyboardSize.height

        let animationDurationValue = info[UIKeyboardAnimationDurationUserInfoKey] as! NSNumber

        var animationDuration : NSTimeInterval = animationDurationValue.doubleValue

        self.keyboardDelegate?.keyboardWillHideWithSize(keyboardSize, andDuration: animationDuration)

        // pull signUpWindow back to its original position
        UIView.animateWithDuration(animationDuration, delay: 0.25, options: UIViewAnimationOptions.CurveEaseInOut, animations: {
            self.signUpWindow.frame = CGRectMake(self.signUpWindowPosition.x, self.signUpWindowPosition.y, self.signUpWindow.bounds.width, self.signUpWindow.bounds.height)
            }, completion: nil)
    }

    func textFieldShouldReturn(textField: UITextField) -> Bool
    {
        switch textField
        {
        case signUpWindow.firstNameTextField:
            signUpWindow.lastNameTextField.becomeFirstResponder()
            break
        case signUpWindow.lastNameTextField:
            signUpWindow.userNameTextField.becomeFirstResponder()
            break
        case signUpWindow.userNameTextField:
            signUpWindow.passwordTextField.becomeFirstResponder()
            break
        case signUpWindow.passwordTextField:
            signUpWindow.confirmPasswordTextField.becomeFirstResponder()
            break
        case signUpWindow.confirmPasswordTextField:
            signUpWindow.emailTextField.becomeFirstResponder()
            break
        default:
            textField.resignFirstResponder()
        }
        return true
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    override func viewWillDisappear(animated: Bool) {
        NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillShowNotification, object: nil)
        NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillHideNotification, object: nil)
    }

    @IBAction func signup()
    {
        signUpWindow.frame=CGRectMake(signUpWindowPosition.x, signUpWindowPosition.y, 485,450)
        signUpWindow.backgroundColor=UIColor.clearColor()

        self.view.addSubview(signUpWindow)
    }
}
Plantain answered 29/4, 2015 at 0:39 Comment(2)
Amy, you're missing a call to super.viewWillDisappear() you probably want in there (but otherwise nice work).Clite
Worked for me, it hard coding I am still finding ways to do it generic way. Thanks for the help.Tecu
F
47

You need to implement UITextFieldDelegate in your class and set that object as the delegate for the UITextField. Then implement the method textFieldShouldReturn: like this:

func textFieldShouldReturn(textField: UITextField) -> Bool {
    textField.resignFirstResponder()
    if textField == someTextField { // Switch focus to other text field
        otherTextField.becomeFirstResponder()
    }
    return true
}

In your example you are missing this line:

confirmPasswordTextField.delegate = self

If you have implemented the delegate of course.

Factor answered 28/4, 2015 at 18:3 Comment(6)
And does textFieldShouldReturn get called?Factor
So I am having some issues replying to show the code the way I want it displayed. I have set the delegate, just earlier. I did implement the UITextFieldDelegate in both the MainView and the SubView. I wrote the code for textFieldShouldReturn, with only a println at the moment to see if it gets called in either the MainView or subView ... neither calls it.Plantain
You should do everything Stefan wrote in SignUpWindowView.swift as long as you creating there text field and setting self as text field delegateSupply
I edited my original post to include an abbreviatef version of SignUpWindowView.swift 1) it shows the implemented delegate 2) it shows textfield.delegate=self and 3) it shows the textFieldShould return function yet I still don't get the textFieldShouldReturn to get called when I tap on the next or done button :(Plantain
You have to move textFieldShouldReturn outside of the drawRect. This way it is inside a function and not inside a class. Protocol won't recognize it and the function won't be called.Factor
For Swift 3, there should be a '_' right before the parameters in the function.Grapple
P
13

I was attempting to test my textfields in the SignUpWindowView.swift, which is where all of the textFields are created. But, since I place SignUpWindowView into my MainViewController as a subview, all of my UITextField "handling" needed to be done in the MainView and NOT its subview.

So here is my entire code (at the moment) for my MainViewController, which handles moving my SignUpWindowView up/down when the keyboard is shown/hidden and then moves from one field to the next. When the user is in the last text field (whose keyboard Next button is now set to Done in the subview) the keyboard tucks away and the user can then submit the form with a signup button.

MainViewController:

import UIKit

@objc protocol ViewControllerDelegate
{
    func keyboardWillShowWithSize(size:CGSize, andDuration duration:NSTimeInterval)
    func keyboardWillHideWithSize(size:CGSize,andDuration duration:NSTimeInterval)
}

class ViewController: UIViewController,UITextFieldDelegate
{
    var keyboardDelegate:ViewControllerDelegate?

    let signUpWindow=SignUpWindowView()
    let signUpWindowPosition:CGPoint=CGPointMake(505, 285)

    override func viewDidLoad()
    {
        super.viewDidLoad()

        // Keyboard Notifications
        NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillShow:", name: UIKeyboardWillShowNotification, object: nil)
        NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillHide:", name: UIKeyboardWillHideNotification, object: nil)

        // set the textFieldDelegates
        signUpWindow.firstNameTextField.delegate=self
        signUpWindow.lastNameTextField.delegate=self
        signUpWindow.userNameTextField.delegate=self
        signUpWindow.passwordTextField.delegate=self
        signUpWindow.confirmPasswordTextField.delegate=self
        signUpWindow.emailTextField.delegate=self
    }


    func keyboardWillShow(notification: NSNotification)
    {
        var info:NSDictionary = notification.userInfo!
        let keyboardFrame = info[UIKeyboardFrameEndUserInfoKey] as! NSValue
        let keyboardSize = keyboardFrame.CGRectValue().size

        var keyboardHeight:CGFloat = keyboardSize.height

        let animationDurationValue = info[UIKeyboardAnimationDurationUserInfoKey] as! NSNumber

        var animationDuration : NSTimeInterval = animationDurationValue.doubleValue

        self.keyboardDelegate?.keyboardWillShowWithSize(keyboardSize, andDuration: animationDuration)

        // push up the signUpWindow
        UIView.animateWithDuration(animationDuration, delay: 0.25, options: UIViewAnimationOptions.CurveEaseInOut, animations: {
            self.signUpWindow.frame = CGRectMake(self.signUpWindowPosition.x, (self.signUpWindowPosition.y - keyboardHeight+140), self.signUpWindow.bounds.width, self.signUpWindow.bounds.height)
            }, completion: nil)
    }

    func keyboardWillHide(notification: NSNotification)
    {
        var info:NSDictionary = notification.userInfo!

        let keyboardFrame = info[UIKeyboardFrameEndUserInfoKey] as! NSValue
        let keyboardSize = keyboardFrame.CGRectValue().size

        var keyboardHeight:CGFloat = keyboardSize.height

        let animationDurationValue = info[UIKeyboardAnimationDurationUserInfoKey] as! NSNumber

        var animationDuration : NSTimeInterval = animationDurationValue.doubleValue

        self.keyboardDelegate?.keyboardWillHideWithSize(keyboardSize, andDuration: animationDuration)

        // pull signUpWindow back to its original position
        UIView.animateWithDuration(animationDuration, delay: 0.25, options: UIViewAnimationOptions.CurveEaseInOut, animations: {
            self.signUpWindow.frame = CGRectMake(self.signUpWindowPosition.x, self.signUpWindowPosition.y, self.signUpWindow.bounds.width, self.signUpWindow.bounds.height)
            }, completion: nil)
    }

    func textFieldShouldReturn(textField: UITextField) -> Bool
    {
        switch textField
        {
        case signUpWindow.firstNameTextField:
            signUpWindow.lastNameTextField.becomeFirstResponder()
            break
        case signUpWindow.lastNameTextField:
            signUpWindow.userNameTextField.becomeFirstResponder()
            break
        case signUpWindow.userNameTextField:
            signUpWindow.passwordTextField.becomeFirstResponder()
            break
        case signUpWindow.passwordTextField:
            signUpWindow.confirmPasswordTextField.becomeFirstResponder()
            break
        case signUpWindow.confirmPasswordTextField:
            signUpWindow.emailTextField.becomeFirstResponder()
            break
        default:
            textField.resignFirstResponder()
        }
        return true
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    override func viewWillDisappear(animated: Bool) {
        NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillShowNotification, object: nil)
        NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillHideNotification, object: nil)
    }

    @IBAction func signup()
    {
        signUpWindow.frame=CGRectMake(signUpWindowPosition.x, signUpWindowPosition.y, 485,450)
        signUpWindow.backgroundColor=UIColor.clearColor()

        self.view.addSubview(signUpWindow)
    }
}
Plantain answered 29/4, 2015 at 0:39 Comment(2)
Amy, you're missing a call to super.viewWillDisappear() you probably want in there (but otherwise nice work).Clite
Worked for me, it hard coding I am still finding ways to do it generic way. Thanks for the help.Tecu
B
7

Using tags makes it easier. Assign tags in ascending order to all the text fields you are using on your screen.


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

    let textTag = textField.tag+1
    if let nextResponder = textField.superview?.viewWithTag(textTag) as UIResponder
    {
        //textField.resignFirstResponder()
        nextResponder.becomeFirstResponder()
    }
    else {
        // stop editing on pressing the done button on the last text field.

        self.view.endEditing(true)
    }
    return true
}
Bandanna answered 29/12, 2017 at 13:53 Comment(2)
it's not working for me nextResponder nil all time after self.view.superview?.viewWithTag(textTag) as UIResponder? change this it's work for meSplashdown
Not working, textField.superview?.viewWithTag(textTag) gives me nil.Tecu
J
1

You connect the DidEndOnExit(I wrote this from memory so maybe its not called this exactly but similar) UIControl event using an @IBAction and in that func you use textF.resignFirstResponder() or .becomeFirstResponder()


EDIT

UITextField is subclass of UIControl and to programatically add a new event you use the addTarget() method. Ex:

func a(sender: AnyObject) {}

textField.addTarget(self, action: "a:", forControlEvents: .EditingDidEndOnExit)

UIControl docs

Janessajanet answered 28/4, 2015 at 17:40 Comment(1)
How do I accomplish that programmatically in Swift. I am not using a storyboard for adding the subview. SignUpWindowView.swift is a class that is then added to the MainView via a sign up button (that is in a storyboard) which runs a function that adds the signUpWindow subview. Does that make sense? I get using the .becomeFirstResponder bit ... I just can't seem to get it to recognize the textFieldShouldReturn function.Plantain

© 2022 - 2024 — McMap. All rights reserved.