Learn how to do typical UIView animations using autolayout constraints.
Using autolayout in your apps is becoming increasingly important with each new version of iOS. If you're used to the old springs and struts model, you may find that some things that you could do in your sleep now pose somewhat of a challenge. One of these for me was UIView animations.
You should definitely at least be familiar with auto-layout to follow this example, or none of it will make sense. I'm only covering the animation portions here.
I've set up a simple interface to demonstrate how to animate an object from partially visible to fully visible. Think of it like an info tab or maybe a view that contains some action buttons. The principles learned with this example can be applied to all sorts of UIView animations.
My interface consists of a UIView with a height of 300, with the top space to the superview set to -220 so that 80 points are showing at the top of the view controller. There is a button at the bottom of this view that will animate it down so the full 300 is showing, and then animate it back up to only 80 showing. Take a look at the constraints for this view in the following image if you want to recreate this example.
I connected the UIView to an outlet and the button to an action on the ViewController that we will use to animate the view.
There are two main steps for animating with autolayout. First, you change the constraints on the view you want to move. Remove constraints you don't need and add the new ones, or in this case all we need to do is update the constant corresponding to the space between our view to animate and the parent view. Our view starts with a spacing of -220.0, and we will change it to 0, and do the opposite for the reverse animation.
The second step is to tell the system we have changed the constraints and want them to be layed out. All we have to do is put this in an animation block and it takes care of moving everything around. The following snippet is all the code we need.
#import "ViewController.h" @interface ViewController () @property (nonatomic, weak) IBOutlet UIView *viewToAnimate; @property (nonatomic, assign) BOOL containerIsOpen; @end @implementation ViewController #pragma mark - View Lifecycle - (void)viewDidLoad { [super viewDidLoad]; self.containerIsOpen = NO; } #pragma mark - Interface Actions - (IBAction)moveButtonTouched { if (self.containerIsOpen) { [self replaceTopConstraintOnView:self.viewToAnimate withConstant:-220.0]; } else { [self replaceTopConstraintOnView:self.viewToAnimate withConstant:0.0]; } [self animateConstraints]; self.containerIsOpen = !self.containerIsOpen; } #pragma mark - Helper Methods - (void)replaceTopConstraintOnView:(UIView *)view withConstant:(float)constant { [self.view.constraints enumerateObjectsUsingBlock:^(NSLayoutConstraint *constraint, NSUInteger idx, BOOL *stop) { if ((constraint.firstItem == view) && (constraint.firstAttribute == NSLayoutAttributeTop)) { constraint.constant = constant; } }]; } - (void)animateConstraints { [UIView animateWithDuration:0.5 animations:^{ [self.view layoutIfNeeded]; }]; } @end
In viewDidLoad we are just setting the container to be closed since on our storyboard we set the view as closed or collapsed.
I added two helper methods to simplify the code. We know that we want to change the top constraint on our view with a different constant depending on opening or closing.
In replaceTopConstraintOnView: withConstant, we use a block to go through the array of constraints on our parent view. If you look at the storyboard you can see that the vertical space constraint of our view to animate is actually a constraint on the view controller's view.
That is why we are enumerating through self.view.constraints and not self.viewToAnimate.constraints. Check out the NSLayoutConstraint documentation to figure out which constraint data you need to look at to identify the constraint you are looking for.
We know that the firstItem should be our viewToAnimate, and we are looking for the NSLayoutAttributeTop as our first attribute. Once we find the constraint we are looking for, we change the constant.
So, when the button is touched, it calls moveButtonTouched. We check if the container is open, or closed, and call our helper method with the proper constant value. Next we call our animateConstraints help method which calls [self.view layoutIfNeeded] inside an animation block. This is the call that will move our view. Finally we flip our boolean so the animation runs in reverse next time.
Pick up the finished project on my GitHub, or just grab the code from my Gist if you can handle the interface.