Link for previous question: UITextField text jumps
Briefly:
I have ViewController
with 2 UITextField
elements. When loginField is firstResp
Here is simple code you need to write for stop jumping of text. This is work for me.
func textFieldDidEndEditing(_ textField: UITextField) {
// Workaround for the jumping text bug.
textField.resignFirstResponder()
textField.layoutIfNeeded()
}
Put this code in your keyboard will show notification, BEFORE you do any animations:
UIView.performWithoutAnimation({
self.yourTextField1.layoutIfNeeded()
self.yourTextField2.layoutIfNeeded()
//etc.
})
Tnx to haluzak:
func textFieldShouldReturn(textField: UITextField) -> Bool {
if textField === self.loginField {
self.passwordField.becomeFirstResponder()
return false
}
return true
}
func keyboardWillShow(notification : NSNotification) {
let keyboardInfo = notification.userInfo!
let keyboardFrame = keyboardInfo[UIKeyboardFrameEndUserInfoKey]!.CGRectValue
let animDuration = keyboardInfo[UIKeyboardAnimationDurationUserInfoKey]!.doubleValue!
UIView.performWithoutAnimation({
self.loginField.layoutIfNeeded()
self.passwordField.layoutIfNeeded()
})
if keyboardFrame.height != self.scrollViewBottom.constant {
UIView.animateWithDuration(animDuration, animations: {
self.scrollViewBottom.constant = keyboardFrame.height
self.view.layoutIfNeeded()
let offsetY = CGRectGetMaxY(self.loginButton.frame) + 10 - self.scrollView.frame.height
if offsetY > 0 {
self.scrollView.contentOffset = CGPointMake(0, offsetY)
}
})
}
}
I've faced same issue, when I was trying to change first responder. There is a better solution for this issue. Just implement UITextField subclass and used in places, where your text field can be animated (keyboard, first respond switching and so on).
@interface UITextFieldFixed : UITextField
@end
@implementation UITextFieldFixed
- (BOOL)resignFirstResponder
{
BOOL result = [super resignFirstResponder];
[self setNeedsLayout];
[self layoutIfNeeded];
return result;
}
@end
More info about issue: https://github.com/foundry/UITextFieldBug
Copy and paste for Swift3...
class UITextFieldFixed: UITextField {
override func resignFirstResponder() -> Bool {
let r = super.resignFirstResponder()
self.setNeedsLayout()
self.layoutIfNeeded()
return r
}
}
Based on your edited question I can see this, when you tap next button on keyboard:
resignFirstResponder()
and then becomeFirstResponder()
. This calls keyboardWillHide
notification and then keyboardWillShow
notificationkeyboardWillHide
you have self.view.layoutIfNeeded()
which layouts the view (and subviews - textfields) without animation. Because of this the textfield layout is "fixed" and when you do animation in keyboardWillShow
the text in textfield doesn't "jump" anymore, because you did layout in keyboardWillHide
.
But when you just tap another textfield, only keyboardWillShow
is called, layout is not "fixed" in textfield and when you animate the view, the text does a "jump" animation.
That's why it doesn't jump when you tap next on keyboard but it does jump when you just tap another textfield.
So I would advise to change it to this:
func textFieldShouldReturn(textField: UITextField) -> Bool {
if textField === self.loginField {
self.passwordField.becomeFirstResponder()
return false
}
return true
}
func keyboardWillShow(notification : NSNotification) {
let keyboardInfo = notification.userInfo!
let keyboardFrame = keyboardInfo[UIKeyboardFrameEndUserInfoKey]!.CGRectValue
let animDuration = keyboardInfo[UIKeyboardAnimationDurationUserInfoKey]!.doubleValue!
self.loginField.layoutIfNeeded()
self.passwordField.layoutIfNeeded()
if keyboardFrame.height != self.scrollViewBottom.constant {
UIView.animateWithDuration(animDuration, animations: {
self.scrollViewBottom.constant = keyboardFrame.height
self.view.layoutIfNeeded()
let offsetY = CGRectGetMaxY(self.loginButton.frame) + 10 - self.scrollView.frame.height
if offsetY > 0 {
self.scrollView.contentOffset = CGPointMake(0, offsetY)
}
})
}
}