5

If I add any kind of UIDynamicBehavior to my views, it completely breaks things when the device is rotated. Here's what it is in portrait (displaying correctly):

Correct Layout

And here it is in landscape, all broke:Broken layout

I don't believe it's an autolayout issue because if I remove the calls to add the UIDynamicBehavior it works fine with no autolayout problems. No autolayout errors are ever thrown either. Here's the code:

@interface SWViewController () {
  UICollisionBehavior *coll;
  UIDynamicAnimator *dynamicAnimator;
}

@implementation SWViewController
- (void)viewDidLoad {
  [super viewDidLoad];
  dynamicAnimator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view];
}

- (void)viewDidAppear:(BOOL)animated {
  [super viewDidAppear:animated];
  [self setupCollisions]; // commenting this out fixes the layout
}

- (void)setupCollisions {
  NSArray *dynamicViews = @[greenView];
  coll = [[UICollisionBehavior alloc] initWithItems:dynamicViews];    
  CGFloat topBound = CGRectGetMinY(greenView.frame);
  [coll addBoundaryWithIdentifier:@"top"
                        fromPoint:CGPointMake(0, h1)
                          toPoint:CGPointMake(CGRectGetWidth(greenView.frame), h1)];    
  [dynamicAnimator addBehavior:coll];
}

If I override didRotateFromInterfaceOrientation I can see that the top boundary of greenView doesn't follow what autolayout says it should (again, removing the call to setupCollisions fixes this).

The autolayout boundaries on greenView are:

height = 200
trailing space to Superview = 0
leading space to Superview = 0
bottom space to Superview = 0
swilliams
  • 48,060
  • 27
  • 100
  • 130
  • Hi, I see that you are using Collision Behavior here. What does this do without Gravity Behavior? Thanks. – Unheilig Oct 10 '13 at 18:27
  • In this example, I'm not even using a Gravity Behavior. If I switch it out it does the same thing. – swilliams Oct 11 '13 at 01:38
  • 1
    I'd highly recommend watching the WWDC videos on the subject. Sessions 206 and 221 from this year. https://developer.apple.com/wwdc/videos/ Do create your own questions on SO if you have them. – swilliams Oct 12 '13 at 01:42

3 Answers3

2

One solution I found was to override willRotateToInterfaceOrientation: and remove the UIDynamicItems and then re-add them in didRotateFromInterfaceOrientation:. This strikes me as rather hacky, and could potentially introduce bugs once I add more complex behaviors.

swilliams
  • 48,060
  • 27
  • 100
  • 130
  • I don't really buy this as an answer for this problem, because by reapplying the behavior to the animator, the animation repeats itself, and to do that on each rotation... not elegant. – Andy Obusek Apr 29 '14 at 01:01
  • I'd love see a better one (please)! Because yes, I had to track the state of things and carefully reapply behaviors so that the animations wouldn't be stuttery and repeat. – swilliams Apr 29 '14 at 06:03
  • I haven't had to revisit this in a while, but I don't believe iOS 8 provided anything new in terms of handling this. – swilliams May 29 '15 at 17:22
0

Dynamic animator is changing frames of involved views. As a result any animation would be disturbed by a call to -[UIView setNeedsLayout] (views would be put to constraint driven positions regardless on dynamic animation state.

My observation is that is you use auto-generated layout constraints the dynamic animator removes them from any view involved in the animation.

If you add your own layout constraints - they persist - but can disturb your animation when view is asked to recalculate layout.

0

Please double check your auto layout constraints.

I had a very similar problem:

In my case, I have a subview, MyView, which had a set of constraints: V:|-(0)-[MyView], V:[MyView]-(0)-|, H:[MyView:(==300)], and it works good with out UIDynamics. But after adding UIDynamics to MyView, the width changed during rotation, which is very similar to your problem.

I fixed it by adding one more constraint: H:|-(0)-[MyView]

Lei Zhao
  • 141
  • 1
  • 6