Search code examples
iosobjective-canimationcalayeruiviewanimation

CALayer transform different when animated


This has been driving me crazy. I am using s CATransform3D to animate the main layer of my view (to apply a perspective effect). I have a function called ApplyPerspective that sets it up, and ClearPerspective which resets the layer's transform property to CATransform3DIdentity.

Here's my code:

    ApplyPerspective(self.mainContentView);

    [UIView animateWithDuration:self.animationDuration
                          delay:0.0
                        options:UIViewAnimationOptionCurveEaseOut
                     animations:^{
                         ClearPerspective(self.mainContentView);
                    }
                     completion:nil];

The issue I am having is that the perspective transform applied by ApplyPerspective is incorrect. If I remove the -animateWithDuration call, but leave the ApplyPerspective call, the layer is rendered as expected. I am not sure why the behavior is changing when animated.

I have tried changing the duration, animation curve, flattening the function calls, but I am unable to trace the source of this error.

Update to add bodies of ApplyPerspective() and ClearPerspective():

void ApplyPerspective(UIView *aView) {

    // Magic numbers derived using debug sliders
    CGFloat zDist, rotRads, xOffset, yOffset, scale, shift;
    if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
        zDist   = 172.5378;
        rotRads = 2.6531;
        xOffset = -13.0;
        yOffset = 402;
        shift   = -241;

    }
    else {
        zDist =     513.4995;
        rotRads =   1.0007;
        xOffset = 0.0;
        yOffset =   -100.0;
        scale = 1.0;
        shift = 0.0;
    }

    CATransform3D p = CATransform3DIdentity;

    p = CATransform3DTranslate(p, xOffset, yOffset, 0.0);

    p.m34 = 1.0 / -zDist;
    p = CATransform3DRotate(p, rotRads, 1.0, 0.0, 0);

    p = CATransform3DTranslate(p, 0, shift, 0.0);

    aView.layer.transform = p;
}

void ClearPerspective(UIView *aView, UIView *parentView) {
    aView.layer.transform = CATransform3DIdentity;
}

Here's the view hierarchy: mainContentView is a child of the ViewController's root view, which uses auto layout. The constraints are such that mainContentView fills the root view. mainContentView has a sibling image view which is used as a background during the animation.


Solution

  • I finally solved it! Apparently some transforms can affect auto layout, and the frame of mainContentView was being modified by the layout engine, even though the transform was being reset to the identity transform.

    The fix is to send -layoutIfNeeded to the parent view (self.view in my case) to force layout, and again after the animation is complete. I am posting this since others may run into something similar.

    If your transforms aren’t looking right, send -layoutIfNeeded.