fs_tigre fs_tigre - 2 months ago 11
Swift Question

Animate views constraint without animating other views

I'm trying to animate (up and down) all of the views in a viewController when the user moves from textField to textField where each textField has a keyboard with a different height and I'm using autolayout to position all of the views.

What I have is four

UITextFields
and three
UIButtons
using autolayout and which move up and down based on the different keyboards-height, my issue is that for some reason all of the views animate as soon as the viewController loads which is not what I want, I only want them to animate when the keyboard's height is different. The animation looks like it is animating all of the view's sizes and positions as soon as the viewController is loaded.

To drive the animation I have a main constraint (
constraintKeyboardHeight
) where all of the views are attached to for determining their vertical position (see image below).

FYI - Please note that I'm auto-focusing one of the textFields in the
viewDidLoad
method. I'm not sure if this has something to do with it.

Any idea? Is this normal behavior when animating views with constraints?

override func viewDidLoad() {
super.viewDidLoad()

oneTextField.becomeFirstResponder()

NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(UserInputViewController.keyboardWillShow(_:)), name: UIKeyboardWillShowNotification, object: nil)
}


func keyboardWillShow(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {

keyboardHeight = keyboardSize.height + 10

UIView.animateWithDuration(3.0, animations: {
self.constraintKeyboardHeight.constant = keyboardHeight
self.view.layoutIfNeeded()
})
}
}


enter image description here

Answer

Calling self.view.layoutIfNeeded will trigger a layout update for everything contained in the view. If this includes subviews you don't want to animate, you should add all of the ones you do want to animate into a container subview. Constrain the container subview based on the keyboard, and call layoutIfNeeded on this subview only.

I'm a little confused by

my issue is that for some reason all of the views animate as soon as the viewController loads which is not what I want, I only want them to animate when the keyboard's height is different

and I'm interpreting the issue as "all of the views animate" where you only want some of them. If the issue is more about the views animating immediately, that of course is going to happen since you set the first responder right when the view loads.

Edit: If you don't want to animate on the first load, you can add a property var firstLoad = true to your controller, then modify your keyboard will show function like so:

func keyboardWillShow(notification: NSNotification) {
    if let keyboardSize = (notification.userInfo [UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
        keyboardHeight = keyboardSize.height + 10
        self.constraintKeyboardHeight.constant = keyboardHeight
        if firstLoad {
            view.layoutIfNeeded()
            firstLoad = false
        } else {
            UIView.animateWithDuration(3.0, animations: {
                self.view.layoutIfNeeded()
            })
        }
    }
}

You'll notice I moved the constraint constant line out of the animation block. You don't actually need this line in the animation block, just the layoutIfNeeded call.