127

I've never worked with autolayout constraints before. I have a small new app I'm working on and noticed that the NIB's views are defaulting to autolayout. So, I figured I'd take the opportunity to work with it and try to figure out where Apple is going with this.

First challenge:

I need to resize an MKMapView and I'd like to animate it to the new position. If I do this the way I'm used to:

[UIView animateWithDuration:1.2f
     animations:^{
         CGRect theFrame = worldView.frame;
         CGRect newFrame = CGRectMake(theFrame.origin.x, theFrame.origin.y, theFrame.size.width, theFrame.size.height - 170);
         worldView.frame = newFrame;
}];

...then the MKMapView will 'snap' back to its original height whenever a sibling view gets updated (in my case a UISegmentedControl's title is being updated [myUISegmentedControl setTitle:newTitle forSegmentAtIndex:0]).

So, what I think I want to do is change the constraints of the MKMapView from being equal to the parent view's hight to being relative to the top of the UISegmentedControl that it was covering: V:[MKMapView]-(16)-[UISegmentedControl]

What I want is for the MKMapView height to shorten so that some controls beneath the map view are revealed. To do so I think I need to change the constraint from a fixed full size view to one where the bottom is constrained to the top of a UISegmentedControl...and I'd like it to animate as view shrinks to new size.

How does one go about this?

Edit - this animation is not animating though the bottom of the view does move up 170 instantly:

    [UIView animateWithDuration:1.2f
         animations:^{
             self.nibMapViewConstraint.constant = -170;

    }];

and the nibMapViewConstraint is wired up in IB to the bottom Vertical Space constraint.

Meltemi
  • 37,979
  • 50
  • 195
  • 293
  • 1
    I know that you can easily change the constant value of the constraint in a [UIView animateWithDuration..] block to animate the height change. You need to create an IBOutlet for that constraint and hook it up in your xib, or otherwise keep a reference to it if you created it in code (or loop through all constraints to look for it). Not sure how to animate relatedBy changes but I've read that you should only change constant and not other values of a constraint (for other values, create a new constraint). – yuf Nov 08 '12 at 20:41
  • Hmm. thought I could to but i'm it's not animating. It changes, successfully, and is in the animation block, but is *not* animating!?! – Meltemi Nov 09 '12 at 00:04
  • Found my answer here: – Meltemi Nov 09 '12 at 00:34
  • 1
    Don't forget the [view layoutIfNeeded], that was my problem too haha. That is the same question that solved my problem. – yuf Nov 09 '12 at 00:50
  • Possible duplicate of [How do I animate constraint changes?](http://stackoverflow.com/questions/12622424/how-do-i-animate-constraint-changes) – Senseful Nov 02 '15 at 20:00

6 Answers6

183

After updating your constraint:

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

Replace self.view with a reference to the containing view.

Kumar KL
  • 15,315
  • 9
  • 38
  • 60
Ed McManus
  • 7,088
  • 2
  • 25
  • 17
  • 4
    that works for the view itself, however the views which have constraints to that view just move instantly. What do I do? ty – Fabio Aug 06 '13 at 11:15
  • 7
    you need to call layout if needed on those views too. however it doesn't really work properly as this will animate the view refresh as well. How do u animate the constraint adjustment only in the other views? – ngb Aug 24 '13 at 12:58
  • 3
    **Beware:** If using the `UIViewAnimationOptionBeginFromCurrentState` the layout constraints will be set *BEFORE* the animation! – Robert Sep 12 '14 at 08:15
  • 14
    Instead of calling `layoutIfNeeded` for each of those views, simply call `[[self.view superview] layoutIfNeeded];` – Flying_Banana Oct 14 '14 at 21:55
  • 2
    @ngb you need to change the constraint constant **inside** the animation block. That way the constraint changes with the animation and you can keep using `UIViewAnimationOptionBeginFromCurrentState`. – Eran Goldin Apr 13 '15 at 11:16
87

This works for me (Both iOS7 and iOS8+). Click on the auto layout constraint you would like to adjust (in interface builder e.g top constraint). Next make this an IBOutlet;

@property (strong, nonatomic) IBOutlet NSLayoutConstraint *topConstraint;

Animate upwards;

    self.topConstraint.constant = -100;    
    [self.viewToAnimate setNeedsUpdateConstraints]; 
    [UIView animateWithDuration:1.5 animations:^{
        [self.viewToAnimate layoutIfNeeded]; 
    }];

Animate back to original place

    self.topConstraint.constant = 0;    
    [self.viewToAnimate setNeedsUpdateConstraints];  
    [UIView animateWithDuration:1.5 animations:^{
        [self.viewToAnimate layoutIfNeeded];
    }];
DevC
  • 6,982
  • 9
  • 45
  • 80
  • 2
    WOW!! This actually really does work! This answer was an eye opener for me. Thank you sooo much! -- Erik – Erik van der Neut Oct 06 '14 at 06:10
  • 2
    @ErikvanderNeut it was for me too, glad it was helpful. Im going to be animating constraints from now on rather than the frames position. – DevC Oct 06 '14 at 08:13
  • 1
    don't miss to add: [containerView layoutIfNeeded]; It ensures that all pending layout operations have been completed BEFORE animateWithDuration block. – ingconti Aug 28 '18 at 07:04
11

There is a very good tutorial from apple itself that explain how to use animation with autolayout. Follow this link and then find the video named "Auto layout by example" It gives some interesting stuff about autolayout and the last part is about how to use animation.

Glenn
  • 140
  • 1
  • 7
3

I have made this small demo available. It shows how auto-layout constraints can be changed and animated in a very simple example. Simply take a look at the DemoViewController.m.

Jens Schwarzer
  • 2,840
  • 1
  • 22
  • 35
1

Most people use autolayout to layout items on their views and modify the layout constrains to create animations.

An easy way to do this without a lot of code is creating the UIView you want to animate in Storyboard and then creating a hidden UIView where you want the UIView to end. You can use the preview in xcode to make sure both UIViews are where you want them to be. After that, hide the ending UIView and swap the layout constraints.

There is a podfile for swapping layout constrains called SBP if you don't want to write it yourself.

Here's a tutorial.

Arjay Waran
  • 433
  • 5
  • 9
0

No need to use more IBOutlet reference of the constraint instead of this you can directly access or update already applied constraint either applied by Programmatically or from Interface Builder on any view using the KVConstraintExtensionsMaster library. This library is also managing the Cumulative behavior of NSLayoutConstraint.

To add Height Constraint on containerView

 CGFloat height = 200;
 [self.containerView applyHeightConstrain:height];

To update Height Constraint of containerView with animation

[self.containerView accessAppliedConstraintByAttribute:NSLayoutAttributeHeight completion:^(NSLayoutConstraint *expectedConstraint){
        if (expectedConstraint) {
            expectedConstraint.constant = 100;

            /* for the animation */ 
            [self.containerView  updateModifyConstraintsWithAnimation:NULL];
      }
    }];
keshav vishwkarma
  • 1,832
  • 13
  • 20