29

I've noticed that in iOS 10 Beta 5 (about to try Beta 6), AutoLayout constraint animation behaves a bit differently.

For example, this approach does not work the same as it did in previous iOS releases:

[view addConstraints:@[constraints...]];

[view setNeedsUpdateConstraints];
[view layoutIfNeeded];

[UIView animateWithDuration:...
{
    /* adjust constraint here... */
    [view layoutIfNeeded]; // Only the adjusted constraints since previous layoutIfNeeded() call should animate change with duration.

} completion:{ ... }];

... In my testing, the constraints initially added with addConstraints() will also animate in iOS 10 with the UIView animateWithDuration() block... which is causing some funky/undesirable behavior so far.

For example, setting the left/right constraints in the array (but the vertical constraints in the block) causes the entire view to animate onto the screen diagonally with this approach... which is totally incorrect.

Does anyone know how to do this correctly for both iOS 9 (and below), as well as 10?

Ben Guild
  • 4,881
  • 7
  • 34
  • 60
  • 1
    This might not be relevant to your issue, but there does seem to be an issue related around animations for iOS 10. However the syntax should remain the same for both iOS 9 and 10. The issue must be from iOS 10's new animation object. https://forums.developer.apple.com/thread/53602 https://openradar.appspot.com/27679031 – cnotethegr8 Aug 23 '16 at 09:44
  • I was able to sort of work around it, but it's not ideal :\ – Ben Guild Aug 23 '16 at 09:46
  • Is your issue related to the tableview height offset? Because I just created a fix for it. If not, my fix might help you, however it involves applying the animation from the layer level. Let me know and I'll put it in an answer. – cnotethegr8 Aug 23 '16 at 09:56
  • Not sure, but it was code in a UITableView header, by coincidence. – Ben Guild Aug 23 '16 at 10:17
  • For what it's worth, I wrote it as a QA post. Good luck. http://stackoverflow.com/questions/39104846/uitableviewcell-animate-height-issue-in-ios-10/ – cnotethegr8 Aug 23 '16 at 15:11

3 Answers3

90

Try calling layoutIfNeeded on the view's superview, instead of the view itself.

--

I had a similar problem, my view was supposed to animate from the top at a 0 height, downward to a > 0 height (i.e. (0, 0, 320, 0) to (0, 0, 320, 44)).

Using Xcode 8 with iOS 10, the animation behavior was much different. Instead of animating downward from the top, the view animates up & down from the vertical center of the destination frame.

I fixed this by, instead of calling layoutIfNeeded on the view itself, calling it on the view's superview. Somehow, the behavior was restored.

Hope it helps!


Helpful comment by @Ramiro:

According to the documentation, layoutIfNeeded lays out the subviews (but doesn't contemplate the current view itself). So, in order to update the current view lay out, you need to call layoutIfNeeded from the super view.

aksh1t
  • 5,410
  • 1
  • 37
  • 55
beebcon
  • 6,893
  • 5
  • 25
  • 27
  • Hmm. I can give it a shot. Does this also work normally in iOS 9 based on your testing? – Ben Guild Sep 15 '16 at 05:48
  • Hmm, you're right. Seems like this is the solution! Even works in iOS 9 for me. – Ben Guild Sep 15 '16 at 07:49
  • Awesome, glad it helped! It's odd the behavior is different. Something similar might work for those with table view cell issues, though I haven't tried – beebcon Sep 15 '16 at 14:39
  • Solved my issues as well. I guess the superview must know about its subviews new constraints for animation to work :/ – Infinity Sep 18 '16 at 03:05
  • @beebcon thank you very much for your answer, it took me some hours to find your solution. – AndaluZ Sep 19 '16 at 09:31
  • This issue used to exist in iOS 7 too. Check this out http://stackoverflow.com/questions/23028701/auto-layout-uiview-on-constraint-change-does-not-animate/23102912#23102912 – BangOperator Oct 01 '16 at 09:01
  • In my case, calling superView didn't work, and I end up calling superView's superView, then it worked.... some of the iOS 10 update are pretty weird.... >_ – RainCast Oct 05 '16 at 06:22
  • Dude this is unbelievable... It took me ages to find a solution for it. Thank you. – Eduardo Urso Nov 04 '16 at 15:57
  • 5
    According to the documentation, layoutIfNeeded lays out the subviews (but doesn't contemplate the current view itself). So, in order to update the current view lay out, you need to call layoutIfNeeded from the super view. – Ramiro Nov 23 '16 at 15:06
  • Thank you for this. I was almost about to pull my hair out over a silly height change animation. +1 I think you should add @Ramiro's comment to your answer because it helps to understand why the behavior is occurring. – aksh1t Mar 04 '17 at 00:26
  • In addition to your answer, I had to add superview to another line `[[[[self myButton] superview] layer] needsLayout]` – Mohammed A. Al Jama May 29 '17 at 10:38
  • "Try calling layoutIfNeeded on the view's superview" This! – sabiland Jul 13 '17 at 06:48
  • Calling layout on super view fixed the issue I was having – WBuck Jan 17 '18 at 12:20
2

In below method :

[super viewDidLoad];

Write this line:

 [self.view layoutIfNeeded];
Sushil Sharma
  • 2,321
  • 3
  • 29
  • 49
Rizwan Shaikh
  • 273
  • 3
  • 8
  • I think it would be better approach to place `[self.view layoutIfNeeded];` right after you did setup your user interface and not right after your view did load. – Baran Nov 25 '17 at 11:17
0

I made a small test changing H and V of a small red view:

self.red_POS_H = NSLayoutConstraint.constraints(withVisualFormat: "H:|-90-[redView]", options: defaultOptions, metrics: nil, views: viewsDictionary)


self.red_POS_V = NSLayoutConstraint.constraints(withVisualFormat: "V:|-30-[redView]", options: defaultOptions, metrics: nil, views: viewsDictionary)

self.view.addConstraints(self.red_POS_V!)
self.view.addConstraints(self.red_POS_H!)

and animated it:

        // we hope is only one:
        let currV = self.red_POS_V![0]
        let currH = self.red_POS_H![0]


        UIView.animate(withDuration: 3) { 
            // Make all constraint changes here

            currV.constant = 100
            currH.constant = 300

            self.view.layoutIfNeeded() // Forces the layout of the subtree animation block and then captures all of the frame changes
        }

red view moved correctly, if You comment out single line in animation, it does work, horizontally or vertically.

Small project avalable, if You need it.

ingconti
  • 10,876
  • 3
  • 61
  • 48