hamobi hamobi - 21 days ago 5
iOS Question

iOS - constraints animate in an unexpected way in viewdidload?

I'm practicing auto layout and learning about animating constraints.

My first question is. If I am dynamically adding views it seems cumbersome to dynamically add their constraints to the parent view as well. Is there any clean way to accomplish a flexible layout where views can be added and removed programmatically? Or would this mean I should probably think of a simpler solution for what I'm trying to accomplish?

Second question. I have created two views, and some constraints in code. I am just trying to resize the height constraint on the first view on load so that it will become shorter, and the second view will shift upwards accordingly.

here is some code:

first = [[UIView alloc]initWithFrame:CGRectZero];

[first setBackgroundColor:[UIColor blueColor]];

[first setTranslatesAutoresizingMaskIntoConstraints:NO];

[self.view addSubview:first];

UIView *second = [[UIView alloc]initWithFrame:CGRectZero];

[second setBackgroundColor:[UIColor redColor]];

[second setTranslatesAutoresizingMaskIntoConstraints:NO];

[self.view addSubview:second];

NSLayoutConstraint *leading = [NSLayoutConstraint constraintWithItem:first attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeLeading multiplier:1 constant:20];

NSLayoutConstraint *trailing = [NSLayoutConstraint constraintWithItem:first attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeTrailing multiplier:1 constant:-20];

top = [NSLayoutConstraint constraintWithItem:first attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeTop multiplier:1 constant:40];

height = [NSLayoutConstraint constraintWithItem:first attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:80];

[self.view addConstraints:@[leading,trailing,top,height]];

[height setConstant:10];

[UIView animateWithDuration:0.5 animations:^{

[self.view layoutIfNeeded];


okay so at the bottom here I run my animation.. my second view is already positioned where it would be at the end of the animation. the first view expands from its top left corner, to its bottom right corner. it animates diagonally and ends up with a height of 10.

Can anyone explain this behavior. I noticed if I assign the constraints, and make them animate on an IBAction (button touch) then it will animate as expected.


Second question first. Why are you animating changing in viewDidLoad? At this point we know the view is loaded, but it probably isn't laid out and definitely not going to be visible to the user; consider the constraint layout changes in the viewDidLayoutSubviews method.

As for the first question. David H's answer is one way... and a perfectly fine way. To give a different option, I use constraintsWithVisualFormat:options:metrics:views: which allows me to specify views, then creates all the necessary constraints across all the views. It can be a much simpler way to create constraints across several views. Depending on exactly what you are doing one way might better suit your needs.

Edit based on comment...

With something where you'll have to break constraints, you'll still have to find and break the constraints before creating the new ones. There is no way around that. You'll either have a reference to the constraint you want to break or have to iterate through all the constraints on an object to find it. A B C goes to A B and C where the constraint between B and C is gone. Using the visual format to put in X might be something like @"[B]-20-[X]-20-[C]" which will create a constraint for a 20 point spacing between B and X and a second constraint which will be a 20 point spacing between X and C. As a note, the visual format above specifies horizontal positioning/spacing only. You would need a second line to specify the vertical constraints.