3

I have a UIView which contains another UIView object called contentView. Inside contentView, I have several UITextField objects. So that the current editing UITextField is always visible and not being hidden by the keyboard, I am altering the constraints on the contentView inside the textFieldDidBeginEditing: method. This means the contentView slides up and down inside the parent UIView, and keeps the relevant UITextField visible. This part is working fine, but here is a code fragment:

- (void)textFieldDidBeginEditing:(UITextField *)textField
    //offset calculation removed for clarity
    NSInteger offset = ....

    self.contentViewTopConstraint.constant = -offset;
    self.contentViewBottomConstraint.constant = offset;
    [self.view setNeedsUpdateConstraints];

    [UIView animateWithDuration:0.3 animations:^{
        [self.view layoutIfNeeded];
    }];
}

I have noticed that if I type some text into the first UITextField, and then tap on the second UITextField, the text in the first UITextField jumps upwards and then back down again. If I disable the animation in the above code fragment, this behaviour goes away. So my guess is that when editing a UITextField, some other constraints are set, which are then altered as focus moves away from that UITextField. As I'm telling the view to update it's constraints in an animated fashion, this causes the text to move around.

Is there some way I can avoid this, but still maintain the animation for moving the contentView up and down?

Edit: Adding an extra [self.view layoutIfNeeded] call before I update any constraints fixed the issue. I'm still not to sure what might have been going on though, or really why this fixed it. Anyone have any insight?

Ben Williams
  • 4,695
  • 9
  • 47
  • 72
  • Nice spot @alku83 thank you! Although I noticed the issue is fixed if I put the `view.layoutIfNeeded()` call AFTER setting a new constraint constant. I suspect a bit of experimentation will be needed by anyone trying this to see what works in their project. Sadly I don't have any insight as to why this works though. – Jon Cox Jul 29 '15 at 14:20
  • I've since learned that the correct way to animate constraints is to call view.layoutIfNeeded, and then inside the animation block, set the constraints, and call view.layoutIfNeed again. This essentially ensures the view is up to date before the animation takes place. – Ben Williams Jul 29 '15 at 23:18
  • You should post that as the answer! – Matt Hudson Sep 22 '15 at 02:07

2 Answers2

4

I have since learned that the correct way to animate constraints is like this:

- (void)textFieldDidBeginEditing:(UITextField *)textField
    //offset calculation removed for clarity
    NSInteger offset = ....

    [self.view layoutIfNeeded];
    self.contentViewTopConstraint.constant = -offset;
    self.contentViewBottomConstraint.constant = offset;

    [UIView animateWithDuration:0.3 animations:^{
        [self.view layoutIfNeeded];
    }];
}

This resolved the issue I was having.

Ben Williams
  • 4,695
  • 9
  • 47
  • 72
0

I am not sure what is wrong with your piece of code, more code would be required for me to tell what is wrong in that, But you can use this piece of code, it will work for you

-(void)addObservers
{
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(keyboardIsShowing:)
                                                 name:UIKeyboardWillShowNotification
                                               object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(keyboardIsHiding:)
                                                 name:UIKeyboardWillHideNotification
                                               object:nil];
}

-(void)removeObservers
{
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
}

- (void)keyboardIsShowing:(NSNotification*)notification
{

    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationDuration:[notification.userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue]];
    [UIView setAnimationCurve:[notification.userInfo[UIKeyboardAnimationCurveUserInfoKey] integerValue]];
    [UIView setAnimationBeginsFromCurrentState:YES];

    int scaleFactor = 0;
    if([oldPasswrdField isFirstResponder])
        scaleFactor = 20;
    else if([newPasswrdField isFirstResponder])
        scaleFactor = 76;
    else if([confirmPasswrdField isFirstResponder])
        scaleFactor = 132;

    self.contentView.frame = CGRectMake(self.contentView.frame.origin.x, self.view.frame.origin.y-scaleFactor,self.contentView.frame.size.width, self.contentView.frame.size.height);


    [UIView commitAnimations];

}

- (void)keyboardIsHiding:(NSNotification*)notification
{

    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationDuration:[notification.userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue]];
    [UIView setAnimationCurve:[notification.userInfo[UIKeyboardAnimationCurveUserInfoKey] integerValue]];
    [UIView setAnimationBeginsFromCurrentState:YES];


    self.contentView.frame = CGRectMake(self.contentView.frame.origin.x, 0, self.contentView.frame.size.width, self.contentView.frame.size.height);

    //self.view.frame = frame;


    [UIView commitAnimations];

}

You see the scalefactor in keyboardIsShowing method. It is needed to be set according to the textfield you are editing. If you can, you can just fetch the current responding text field and take it's origin.y and set the scale factor according to that. Also, call addObservers in viewDidAppear and call removeObservers in viewDidDisappear. Or according to your needs, but don't forget to call them both once.

Edit: Tell me if this works for you or if you need further help on the matter, would be glad to assist.

Abdul91
  • 708
  • 5
  • 16
  • Thanks for the input, but I believe the issue is related to auto layout and constraints, so setting the frame isn't going to help. – Ben Williams Mar 20 '15 at 07:46
  • if the objective was only to keep uitextfield which is being edited visible and not hide behind the keyboard, this piece of code should be sufficient. Maybe I missed some point in your question. Can you confirm if I misunderstood something? – Abdul91 Mar 20 '15 at 07:57
  • I cannot set the frame manually if I am using auto layout. I would need to update the constraints, and doing this in an animated fashion is what was causing the problem. – Ben Williams Mar 20 '15 at 21:20