Question

I tried to create a CATransform3D, but got it wrong. The following code does what I do want, using key-values:

[_transitionLayer setValue:[NSNumber numberWithFloat:metrics.translationPointsX] 
    forKeyPath:@"sublayerTransform.translation.x"];
[_transitionLayer setValue:[NSNumber numberWithFloat:metrics.radians] 
    forKeyPath:@"sublayerTransform.rotation.y"];
[_transitionLayer setValue:[NSNumber numberWithFloat:metrics.translationPointsZ] 
    forKeyPath:@"sublayerTransform.translation.z"];

right

And here's how I tried to set it up doing CATransform3D:

subLayerTransform.m34 = -0.000555;
CATransform3D subLayerTransform = CATransform3DMakeTranslation(0, 0, 0);
subLayerTransform = 
    CATransform3DTranslate(subLayerTransform, metrics.translationPointsX, 0, 0);
subLayerTransform = 
    CATransform3DRotate(subLayerTransform, metrics.radians, 0, 1, 0);
subLayerTransform = 
    CATransform3DTranslate(subLayerTransform, 0, 0, metrics.translationPointsZ);

wrong

How can I create the matching transform to the one using the key-paths ?

Was it helpful?

Solution

Looking at your two screenshots, and assuming that you're going for a rotating cube effect and the two screenshots are taken at slightly different times in the animation, it looks like the top one has perspective on it and the bottom one does not.

Fixing this is a simple matter of editing the .m34 value of the transform, usually to a small value such as 1/500. There is an excellent write up of this here.

Having done that you need to apply the translation steps before the rotation, like so:

CATransform3D subLayerTransform = CATransform3DMakeTranslation(0, 0, 0);
subLayerTransform.m34 = -0.000555;
subLayerTransform = CATransform3DTranslate(subLayerTransform, _currentMetrics.translationPointsX, 0, _currentMetrics.translationPointsZ);
subLayerTransform = CATransform3DRotate(subLayerTransform, _currentMetrics.radians, 0, 1, 0);

I've done something similar but used a container view controller to shift from one to the next. When commenting on your code I thought that I'd used the z anchor point but it turns out that I didn't. Here is the code I use to transition from one view controller to another as if you were rotating a cube. It may be of interest. This is just an extract from the larger method, hopefully I've not missed anything vital.

CGRect newFrame = self.containerView.bounds;
UIView *outgoingView = _currentViewController.view;
UIView *incomingView = currentViewController.view;

incomingView.frame = newFrame;

[CATransaction begin];
[CATransaction setDisableActions:YES];

CATransform3D parent = CATransform3DIdentity;
parent.m34 = -0.003;
self.containerView.layer.sublayerTransform = parent;

BOOL rightToLeft = (_currentViewController == [self.viewControllers itemBefore:currentViewController]);

CGFloat inTranslateX = incomingView.bounds.size.width;
CGFloat inRotation = M_PI_2;
CGFloat inAnchorX = 0.0;
if (!rightToLeft)
{
    inTranslateX = -inTranslateX;
    inRotation = -inRotation;
    inAnchorX = 1.0;
}


CATransform3D inT = CATransform3DMakeTranslation(inTranslateX, 0.0, 0.0);
inT = CATransform3DRotate(inT, inRotation, 0.0, 1.0, 0.0);
CGRect previousFrame = incomingView.frame;
incomingView.layer.anchorPoint = CGPointMake(inAnchorX, 0.5);
incomingView.frame = previousFrame;
incomingView.layer.transform = inT;
incomingView.hidden = NO;

CGFloat outTranslateX = -outgoingView.bounds.size.width;
CGFloat outRotation = -M_PI_2;
CGFloat outAnchorX = 1.0;
if (!rightToLeft)
{
    outTranslateX = -outTranslateX;
    outRotation = -outRotation;
    outAnchorX = 0.0;
}

CATransform3D outT = CATransform3DMakeTranslation(outTranslateX, 0.0, 0.0);
outT = CATransform3DRotate(outT, outRotation, 0.0, 0.5, 0.0);
CGRect outgoingFrame = outgoingView.frame;
outgoingView.layer.anchorPoint = CGPointMake(outAnchorX, 0.5);
outgoingView.frame = outgoingFrame;
[CATransaction commit];

[self transitionFromViewController:_currentViewController
                  toViewController:currentViewController
                          duration:0.4
                           options:UIViewAnimationCurveEaseInOut
                        animations:^{
                            outgoingView.layer.transform = outT;
                            incomingView.layer.transform = CATransform3DIdentity;
                        }
                        completion:^(BOOL finished) {
                            _currentViewController = currentViewController;
                            outgoingView.layer.transform = CATransform3DIdentity;
                            self.imageView.image = currentViewController.tabBarItem.image;
                            self.viewSelector.userInteractionEnabled = YES;
                        }];

For experimenting with 3D transforms, I have created a little demo app that allows you to build up a stack of transforms and see the effects. It's available on GitHub, and introduced here.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top