50

I have ViewController with 2 UITextField elements: Login and Password. I set delegate for these fields, which includes code below:

func textFieldShouldReturn(textField: UITextField) -> Bool {
    if textField === self.loginField {
        self.loginField.resignFirstResponder()
        self.passwordField.becomeFirstResponder()
        return false
    }

    return true
}

This logic should switch user from login text field to password when he presses Next button on keyboard. But I stuck with glitch: after

self.passwordField.becomeFirstResponder()

text in login field jumps to the top left corner and back. And what's more strange: this glitch reproduces only first time, then you need recreate ViewController to observe this behavior

Here is video of the glitch http://tinypic.com/player.php?v=6nsemw%3E&s=8#.VgVb3cuqpHx

I ended up with this:

func textFieldShouldReturn(textField: UITextField) -> Bool {
    if textField === self.loginField {
        self.loginField.resignFirstResponder()
        // Shitty workaround. Hi, Apple!
        self.loginField.setNeedsLayout()
        self.loginField.layoutIfNeeded()

        self.passwordField.becomeFirstResponder()
        return false
    }

    return true
}
user3237732
  • 1,976
  • 2
  • 21
  • 28

8 Answers8

52

Based on some of the other ideas posted here, this is a solution that is easy to implement, works (for me) in all cases, and doesn't appear to have any side effects:

- (void)textFieldDidEndEditing:(UITextField *)textField
{
    // Workaround for the jumping text bug.
    [textField resignFirstResponder];
    [textField layoutIfNeeded];
}

This solution works both if you're going to the next field programmatically from -textFieldShouldReturn: or if the user just touches another responder.

Dave Batton
  • 8,795
  • 1
  • 46
  • 50
17

In a UITextField subclass you can do the following:

-(BOOL)resignFirstResponder
{
    BOOL resigned = [super resignFirstResponder];
    [self layoutIfNeeded];
    return resigned;
}

The trick here is to make sure you call layoutIfNeeded after resignFirstResponder has been called.

Doing it this way is quite handy because you don't need to call resignFirstResponder in the delegate callbacks yourself as this caused me problems inside a UIScrollView, the above however doesn't :)

liamnichols
  • 12,419
  • 2
  • 43
  • 62
9
func textFieldDidEndEditing(_ textField: UITextField) {

    textField.layoutIfNeeded()
}
IT Gypsy
  • 658
  • 8
  • 16
  • While this code may answer the question, providing additional context regarding how and/or why it solves the problem would improve the answer's long-term value. – Donald Duck Mar 03 '17 at 12:20
8

Use this code to avoid the jumping of text in UITextField.

- (BOOL) textFieldShouldReturn:(UITextField *)textField{

      if(textField == self.txtUserName){
        [self.txtEmail becomeFirstResponder];
      }
      else if (textField == self.txtEmail){
        [self.txtPassword becomeFirstResponder];
      }
      else if (textField == self.txtPassword){
        [self.txtConfirmPassword becomeFirstResponder];
      }
      else if (textField == self.txtConfirmPassword){
        [self.txtFirstName becomeFirstResponder];
      }
      else{
        [textField resignFirstResponder];
      }

   return YES;
}

- (void)textFieldDidEndEditing:(UITextField *)textField
{
   [textField resignFirstResponder];
   [textField layoutIfNeeded];
}
Himanshu Mahajan
  • 4,779
  • 2
  • 36
  • 29
6

I'm also facing the same problem. The below code is worked for me.

func textFieldDidEndEditing(_ textField: UITextField) {

    textField.layoutIfNeeded()
}
Ramakrishna
  • 712
  • 8
  • 26
2

More "generic" option is to use notifications right inside your UITextField subclass:

 - (void)setupJumpingTextWorkaround {
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(forceLayout)
                                                 name:UITextFieldTextDidEndEditingNotification object:self];
}

- (void)forceLayout {
        [self setNeedsLayout];
        [self layoutIfNeeded];
    }

Do not forget to unsubscribe

d.lebedev
  • 2,305
  • 19
  • 27
2

Based on what I understand from this:

This issue can be caused when you have layout changes or animations handled in the callbacks for keyboard will show and hide notifications (usually in cases when you want the textfield to be pushed up so the keyboard won't hide it).

Solution: I was facing this problem as I was doing layoutIfNeeded every time keyboard will show got called assuming its safe, obviously its not, so when I put a check to do that only when there is a need for changing the frames, the jump stopped.

Community
  • 1
  • 1
Santhosh R
  • 1,518
  • 2
  • 10
  • 14
0

I don't give my text fields delegates. Instead I create an IBAction and attach it to the "Did End On Exit" event. The glitch happens with this method too, but only in iOS 9. It looks to be an OS bug.

My action looks like this:

@IBAction func textFieldAction(sender: UITextField) {
    if sender === usernameField {
        passwordField.becomeFirstResponder()
    }
}

With the above, the glitch happens, but when I do the below the glitch goes away:

@IBAction func textFieldAction(sender: UITextField) {
    if sender === usernameField {
        sender.resignFirstResponder()
        passwordField.becomeFirstResponder()
    }
}

I don't seem to need to call setNeedsLayout().

ovejka
  • 999
  • 1
  • 16
  • 21
Daniel T.
  • 32,821
  • 6
  • 50
  • 72