69

I'm designing an iOS app and I want that when the return key is pressed in my iPhone it directs me to the next following text field.

I have found a couple of similar questions, with excellent answers around but they all just happen to be in Objective-C and I'm looking for Swift code, now this is what I have up until now:

func textFieldShouldReturn(emaillabel: UITextField) -> Bool{
    return true
}

It's placed in the file that's connected and controller to the UIView that contains the text fields, but I'm not sure if thats the right place.

Okay, so I tried this out and got this error:
//could not find an overload for '!=' that accepts the supplied arguments

func textFieldShouldReturn(textField: UITextField) -> Bool {
    let nextTag: NSInteger = textField.tag + 1
    // Try to find next responder
    let nextResponder: UIResponder = textField.superview!.viewWithTag(nextTag)!
    if (nextResponder != nil) {
        // could not find an overload for '!=' that accepts the supplied arguments

        // Found next responder, so set it.
        nextResponder.becomeFirstResponder()
    } else {
        // Not found, so remove keyboard.
        textField.resignFirstResponder()
    }
    return false // We do not want UITextField to insert line-breaks.
}
Eric Aya
  • 68,765
  • 33
  • 165
  • 232
lucas rodriguez
  • 900
  • 2
  • 8
  • 16

16 Answers16

156

Make sure your UITextField delegates are set and the tags are incremented properly. This can also be done through the Interface Builder.

Here's a link to an Obj-C post I found: How to navigate through textfields (Next / Done Buttons)

class ViewController: UIViewController,UITextFieldDelegate {
   // Link each UITextField (Not necessary if delegate and tag are set in Interface Builder)
   @IBOutlet weak var someTextField: UITextField!

   override func viewDidLoad() {
      super.viewDidLoad()
      // Do the next two lines for each UITextField here or in the Interface Builder
      someTextField.delegate = self
      someTextField.tag = 0 //Increment accordingly
   }

   func textFieldShouldReturn(_ textField: UITextField) -> Bool {
      // Try to find next responder
      if let nextField = textField.superview?.viewWithTag(textField.tag + 1) as? UITextField {
         nextField.becomeFirstResponder()
      } else {
         // Not found, so remove keyboard.
         textField.resignFirstResponder()
      }
      // Do not add a line break
      return false
   }
}
Caleb
  • 5,210
  • 2
  • 22
  • 32
  • How would I set them, Im so new at this and quite lost at the moment to be honest – lucas rodriguez Aug 02 '15 at 01:45
  • 1
    By "them" I'm going to assume that you mean the delegates. Easiest way is to do this through the IB. Go to your storyboard->Click on each UITextField->Drag delegate over to the yellow ViewController icon. If you meant the tags, then you can also do this through the IB. Click on each UITextField and set the tags starting at 0 and increment by 1. You can also set both the delegates and tags programmatically in the ViewControllers viewDidLoad by setting textField.delegate = self and textField.tag = n – Caleb Aug 02 '15 at 01:56
  • Post errors please. I made an edit and changed UIResponder to UIResponder! – Caleb Aug 03 '15 at 00:12
  • I added a link to a sample project. Feel free to take a look and let me know if you get it solved. FYI: I set the delegates and the tags through the IB. – Caleb Aug 03 '15 at 00:17
  • Well now when it gets to the last text field and hit return the app crashes 'unexpectedly found nil while unwrapping an Optional value' – lucas rodriguez Aug 03 '15 at 00:25
  • When I comment out the code it doesn't return this error and it works fine – lucas rodriguez Aug 03 '15 at 00:27
  • Im getting a warning here though ' if let nextResponder = textField.superview!.viewWithTag(nextTag) as? UIResponder! {' conditional cast from UIView? to UIResponder always Succeed – lucas rodriguez Aug 03 '15 at 00:40
  • I ignored the warning at first because I didn't have a quick solution and I still worked, but I have since updated the post and sample project to fix the warning. – Caleb Aug 03 '15 at 00:44
  • Isn't it better to directly check the textfield reference instead of setting a tag and check the tag ? – meteorSD Feb 02 '17 at 17:19
  • 2
    @meteorSD With an array of textfields? I am not sure if either practice is better than the other. Tags seem to be the easiest way when relying mostly on the Interface Builder. – Caleb Feb 02 '17 at 17:31
  • it's not perfect work when we use nested stack view. one stack view contain 2 text field and another stack view contain 5 text field . than it go else . and remove keyboard – Harshil Kotecha Feb 25 '17 at 11:30
  • This is a trash solution smh. Should not be using tags. – boidkan Sep 21 '18 at 20:23
  • @boidkan Feel free to add a better solution. I'm sure others who come here in the future would appreciate it! – Caleb Sep 26 '18 at 03:51
40

Swift 5

You can easily switch to another TextField when clicking return key in keyboard.

  • First, Your view controller conforms to UITextFieldDelegate and add the textFieldShouldReturn(_:) delegate method in ViewController
  • Drag from TextField to ViewController in Interface Builder. Then select the delegate option. Note : Do this for all TextField
  • Create an IBOutlet for all TextFields

    class ViewController: UIViewController, UITextFieldDelegate {
    
      @IBOutlet weak var txtFieldName: UITextField!
      @IBOutlet weak var txtFieldEmail: UITextField!
      @IBOutlet weak var txtFieldPassword: UITextField!
    
      func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        if textField == txtFieldName {
           textField.resignFirstResponder()
           txtFieldEmail.becomeFirstResponder()
        } else if textField == txtFieldEmail {
           textField.resignFirstResponder()
           txtFieldPassword.becomeFirstResponder()
        } else if textField == txtFieldPassword {
           textField.resignFirstResponder()
        }
       return true
      }
    }
    
Guilherme Matuella
  • 1,630
  • 1
  • 13
  • 26
Vinoth Vino
  • 6,991
  • 3
  • 54
  • 59
  • 1
    Worked good. But avoiding the line of resignFirstResponder() will be good if there is a call for becomeFirstResponder() because my app shows some jerking when keyboard goes and comes back. – Abdul Saleem Jun 07 '20 at 11:33
19

I suggest that you should use switch statement in textFieldShouldReturn(_:).

// MARK: UITextFieldDelegate

func textFieldShouldReturn(_ textField: UITextField) -> Bool {
    switch textField {
    case nameTextField:
        phoneTextField.becomeFirstResponder()
    case phoneTextField:
        emailTextField.becomeFirstResponder()
    case emailTextField:
        descriptionTextField.becomeFirstResponder()
    default:
        textField.resignFirstResponder()
    }
    return false
}
kubacizek
  • 470
  • 4
  • 9
9

This approach needs some changes in table views and collection views, but it's okay for simple forms I guess.

Connect your textFields to one IBOutletCollection, sort it by its y coordinate and in textFieldShouldReturn(_:) just jump to the next textfield until you reach the end:

@IBOutlet var textFields: [UITextField]!

...

textFields.sortInPlace { $0.frame.origin.y < $1.frame.origin.y }

...

func textFieldShouldReturn(textField: UITextField) -> Bool {
    if let currentIndex = textFields.indexOf(textField) where currentIndex < textFields.count-1 {
        textFields[currentIndex+1].becomeFirstResponder()
    } else {
        textField.resignFirstResponder()
    }
    return true
}    

Or just look at sample project (xcode 7 beta 4)

libec
  • 1,515
  • 1
  • 10
  • 19
  • I'm not very sure I understand the coordinates part could you explain please? – lucas rodriguez Aug 02 '15 at 01:39
  • 1
    sure. You wanna jump from the one on top to the one on the very bottom. So you sort it by y coordinate in array so in array[0] is the one on top, array[1] one under it and array[last] is the one on the bottom. Does this suffice? – libec Aug 02 '15 at 07:56
  • The sorting is necessary because the text fields are not guaranteed to be ordered in the outlet collection: https://medium.com/@abhimuralidharan/what-is-an-iboutletcollection-in-ios-78cfbc4080a1 – agirault Nov 08 '19 at 14:29
4

I have tried many codes and finally this worked for me in Swift 3.0 Latest [March 2017]

The "ViewController" class should inherited the "UITextFieldDelegate" for making this code working.

class ViewController: UIViewController,UITextFieldDelegate  

Add the Text field with the Proper Tag nuber and this tag number is used to take the control to appropriate text field based on incremental tag number assigned to it.

override func viewDidLoad() {

 userNameTextField.delegate = self

        userNameTextField.tag = 0

        userNameTextField.returnKeyType = UIReturnKeyType.next

        passwordTextField.delegate = self

        passwordTextField.tag = 1


        passwordTextField.returnKeyType = UIReturnKeyType.go

}

In the above code, the "returnKeyType = UIReturnKeyType.next" where will make the Key pad return key to display as "Next" you also have other options as "Join/Go" etc, based on your application change the values.

This "textFieldShouldReturn" is a method of UITextFieldDelegate controlled and here we have next field selection based on the Tag value incrementation

func textFieldShouldReturn(_ textField: UITextField) -> Bool

    {

        if let nextField = textField.superview?.viewWithTag(textField.tag + 1) as? UITextField {

            nextField.becomeFirstResponder()

        } else {

            textField.resignFirstResponder()

            return true;

        }

        return false

    }
BHUVANESH MOHANKUMAR
  • 2,517
  • 1
  • 30
  • 33
4

Caleb's version in Swift 4.0

func textFieldShouldReturn(_ textField: UITextField) -> Bool {
    if let nextField = self.view.viewWithTag(textField.tag + 1) as? UITextField {
        nextField.becomeFirstResponder()
    } else {
        textField.resignFirstResponder()
    }
    return false
}

P.S. textField.superview? not working for me

coldembrace
  • 469
  • 5
  • 17
4

Swift & Programmatically

class MyViewController: UIViewController, UITextFieldDelegate {

    let textFieldA = UITextField()
    let textFieldB = UITextField()
    let textFieldC = UITextField()
    let textFieldD = UITextField()

    var textFields: [UITextField] {
        return [textFieldA, textFieldB, textFieldC, textFieldD]
    }

    override func viewDidLoad() {
        // layout textfields somewhere 
        // then set delegate
        textFields.forEach { $0.delegate = self }
    }

    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        if let selectedTextFieldIndex = textFields.firstIndex(of: textField), selectedTextFieldIndex < textFields.count - 1 {
            textFields[selectedTextFieldIndex + 1].becomeFirstResponder()
        } else {
            textField.resignFirstResponder() // last textfield, dismiss keyboard directly
        }
        return true
    }
}
10623169
  • 726
  • 1
  • 8
  • 20
Tieda Wei
  • 558
  • 4
  • 13
3

the easiest way to change to next text Field is this no need for long code

override func viewDidLoad() {
        super.viewDidLoad()

        emailTextField.delegate = self
        passwordTextField.delegate = self
    }


    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        if textField == emailTextField {
            passwordTextField.becomeFirstResponder()
        }else {
            passwordTextField.resignFirstResponder()
        }
            return true
    }
mazenqp
  • 162
  • 1
  • 15
2

Just use becomeFirstResponder() method of UIResponder class in your textFieldShouldReturn method. Every UIView objects are UIResponder's subclasses.

if self.emaillabel.isEqual(self.anotherTextField)
{
    self.anotherTextField.becomeFirstResponder()
}

You can find more information about becomeFirstResponder() method at Apple Doc's in here.

Candost
  • 1,021
  • 1
  • 13
  • 27
2

I have a good solution for your question.

STEP:

1 - Set your return key from the storyboard.

enter image description here

2 - In your swift file.

func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        if textField.returnKeyType == .next {
            Email.resignFirstResponder()
            Password.becomeFirstResponder()
        } else if textField.returnKeyType == .go {
            Password.resignFirstResponder()
            self.Login_Action()
        }
        return true
    }

3 - Don't forget to set the delegate of the Textfield.

Thank you :)

Parth Patel
  • 808
  • 8
  • 26
2

Swift 4.2

This is a More Generic and Easiest Solution, you can use this code with any amount of TextFields. Just inherit UITextFieldDelegate and update the Textfield Tag according to the order and copy this function

func textFieldShouldReturn(_ textField: UITextField) -> Bool {
    let txtTag:Int = textField.tag

    if let textFieldNxt = self.view.viewWithTag(txtTag+1) as? UITextField {
        textFieldNxt.becomeFirstResponder()
    }else{
        textField.resignFirstResponder()
    }

    return true
}
Lahiru Pinto
  • 1,317
  • 14
  • 19
1

An alternative method for purists who don't like using tags, and wants the UITextField delegate to be the cell to keep the components separated or uni-directional...

  1. Create a new protocol to link the Cell's and the TableViewController.

    protocol CellResponder {
      func setNextResponder(_ fromCell: UITableViewCell)
    }
    
  2. Add the protocol to your cell, where your TextField Delegate is also the cell (I do this in the Storyboard).

    class MyTableViewCell: UITableViewCell, UITextFieldDelegate {
      var responder: CellResponder?
    
      func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        responder?.setNextResponder(self)
        return true
      }
    }
    
  3. Make your TableViewController conform to the CellResponder protocol (i.e. class MyTableViewController: UITableViewController, CellResponder) and implement the method as you wish. I.e. if you have different cell types then you could do this, likewise you could pass in the IndexPath, use a tag, etc.. Don't forget to set cell.responder = self in cellForRow..

    func setNextResponder(_ fromCell: UITableViewCell) {
      if fromCell is MyTableViewCell, let nextCell = tableView.cellForRow(at: IndexPath(row: 1, section: 0)) as? MySecondTableViewCell {
    
        nextCell.aTextField?.becomeFirstResponder()
    
      } ....
    }
    
mm282
  • 413
  • 3
  • 7
1

No any special, here is my currently using to change the textFiled. So the code in ViewController looks good :). #Swift4

final class SomeTextFiled: UITextField {

  public var actionKeyboardReturn: (() -> ())?

  override init(frame: CGRect) {
      super.init(frame: frame)
      super.delegate = self
  }

  required init?(coder aDecoder: NSCoder) {
      super.init(coder: aDecoder)
      fatalError("init(coder:) has not been implemented")
  }

  func textFieldShouldReturn(_ textField: UITextField) -> Bool {
      self.resignFirstResponder()
      actionKeyboardReturn?()
      return true
   }
}

extension SomeTextFiled: UITextFieldDelegate {}


class MyViewController : UIViewController {

    var tfName: SomeTextFiled!
    var tfEmail: SomeTextFiled!
    var tfPassword: SomeTextFiled!

    override func viewDidLoad() {
        super.viewDidLoad()
        tfName = SomeTextFiled(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
        tfName.actionKeyboardReturn = { [weak self] in
            self?.tfEmail.becomeFirstResponder()
        }
        tfEmail = SomeTextFiled(frame: CGRect(x: 100, y: 0, width: 100, height: 100))
        tfEmail.actionKeyboardReturn = { [weak self] in
            self?.tfPassword.becomeFirstResponder()
        }
        tfPassword = SomeTextFiled(frame: CGRect(x: 200, y: 0, width: 100, height: 100))
        tfPassword.actionKeyboardReturn = {
            /// Do some further code
        }
    }
}
Stephen Chen
  • 2,851
  • 2
  • 24
  • 34
1

If you have a lot of textfield components my be it could be better to use an outlet collection, linking textfields and setting Return Key from interface builder

@IBOutlet var formTextFields: [UITextField]!

override func viewDidLoad() {
  for textField in formTextFields {
    textField.delegate = self
  }
}

extension RegisterViewController: UITextFieldDelegate {
  func textFieldShouldReturn(_ textField: UITextField) -> Bool {
    if let componentIndex = formTextFields.firstIndex(of: textField) {
      if textField.returnKeyType == .next,
        componentIndex < (formTextFields.count - 1) {
        formTextFields[componentIndex + 1].becomeFirstResponder()
      } else {
        textField.resignFirstResponder()
      }
    }
    return true
  }
}
0

You can go with field tags. I think that's easier than other.

First of all you have enter code hereto give tag to your field.

On my code usernameField tag is 0 and passwordField tag is 1. And I check my tag. Then doing proccess.

func textFieldShouldReturn(_ textField: UITextField) -> Bool {
    if textField.tag == 0 {
        passwordField.becomeFirstResponder()
    } else if textField.tag == 1 {
        self.view.endEditing(true)
        loginFunc()
    } else {
        print("Hata var")

    }
     return false
}

If click return on username field, go password. Or If you click return when password field, run login function to login.

scankiran
  • 9
  • 3
0

Swift 4+ This piece of code will help you.

class YOURClass: UITextFieldDelegate {
  override func viewDidLoad() {

    //delegate your textfield in here
     choosenTextField1.delegate = self
     choosenTextField2.delegate = self

    }

  func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        switch textField.tag {
          case 1:
            choosenTextField1.becomeFirstResponder()
          case 2:
            choosenTextField2.becomeFirstResponder()
          default:
            break   
        }
        
        return true
  }
}
Ali Qaderi
  • 175
  • 3
  • 13