# Loop Animation on several UIButton's NSLayoutConstraint

I took the belated plunge to look at Autolayout. Normally I'd animate based on a UIView's frame (i.e I'd have used the following):

    NSArray *sortedArray = [self.buttonArray sortedArrayUsingDescriptors:[NSArray arrayWithObject:ascendingSort]];

for (int i = 0;i<sortedArray.count;i++) {

UIButton * theButton = [sortedArray objectAtIndex:i];

[UIView animateWithDuration:1 delay:i *0.05 usingSpringWithDamping:10 initialSpringVelocity:20 options:UIViewAnimationOptionCurveEaseIn animations:^{
[theButton setFrame:frameToAnimateTo];
} completion:nil];
}


Am I right in thinking that I now have to animate based on the Constant property of a buttons constraint?

NSArray *sortedArray = [self.ButtonHorizonalConstraints sortedArrayUsingDescriptors:[NSArray arrayWithObject:ascendingSort]];

for (int i = 0;i<sortedArray.count;i++) {

NSLayoutConstraint * constraint = [sortedArray objectAtIndex:i];
UIButton * theButton = constraint.firstItem;

constraint.constant = toShow?0:-theButton.frame.size.width;
[self.view setNeedsUpdateConstraints];

[UIView animateWithDuration:1 delay:i *(toShow?0.1:0.05) usingSpringWithDamping:10 initialSpringVelocity:toShow?20:-20 options:UIViewAnimationOptionCurveEaseIn animations:^{
[self.view layoutIfNeeded];
} completion:nil];
}


The end result I get is what I desire, but something doesn't seem right.

• How many elements in your buttons array? – sha Sep 7 '14 at 16:49
• @sha 10 at present but its dynamic based upon values I get back from some Web API backend – burrows111 Sep 9 '14 at 9:14
• I don't think you want to call animateWithDuration inside the loop. Instead try to call beginAnimation before, set all changes in frame in the loop and call commitAnimation after it. – sha Sep 9 '14 at 13:45

I'm not completely familiar with animating auto-layout constraints. Fortunately (or unfortunately), I've not yet ever had the need to do this. So, I'm not sure if there's a better way, but I'd say if it's working, that's a good sign.

constraint.constant = toShow?0:-theButton.frame.size.width;
[self.view setNeedsUpdateConstraints];


I don't mind ternary operators. But I think you really need to include some white space. When it's all smushed together, and including the negative sign, I had to do a double take before I realized what's going on.

constraint.constant = toShow ? 0 : -theButton.frame.size.width;


[UIView animateWithDuration:1 delay:i *(toShow?0.1:0.05) usingSpringWithDamping:10 initialSpringVelocity:toShow?20:-20


options:UIViewAnimationOptionCurveEaseIn animations:^{ [self.view layoutIfNeeded]; } completion:nil];

This method call suffers the same problems as before with the smushed ternary operator, except it's worse because this method has so many more arguments and you're calculating so much in line and you're not using any vertical space.

I'd rewrite this as such:

CGFloat animationDelay = i * (toShow ? 0.1 : 0.05);
CGFloat springVelocity = toShow ? 20 : -20;

[UIView animateWithDuration:1
delay:animationDelay
usingSpringWithDamping:10
initialSpringVelocity:springVelocity
options:UIViewAnimationOptionCurveEaseIn
animations:^{

[self.view layoutIfNeeded];

} completion:nil];


With that spacing, even if we kept the ternaries in-line, it's still significantly more readable with a single argument per line.

• Thank you for taking the time to pick back up on that! :) – burrows111 Sep 1 '14 at 7:45