iOS CATransform3D Coordinates

2019-04-12 12:41发布

问题:

Would really appreciate any help on this one. I have applied a 3D transformation on a view and need to identify the edge coordinates of the rendered view so I can present another view adjacent to it (without any pixels gap). Specifically I want a series of views ("pages") to fold-up like a leaflet, by animating the angle.

    int dir = (isOddNumberedPage ? 1 : -1);
    float angle = 10.0;

    theView.frame = CGRectMake(pageNumber * 320, 0, 320, 460);        
    CATransform3D rotationAndPerspectiveTransform = CATransform3DIdentity;
    rotationAndPerspectiveTransform.m34 = -1.0 / 2000; // Perspective
    rotationAndPerspectiveTransform = CATransform3DRotate(rotationAndPerspectiveTransform, 
           dir * angle / (180.0 / M_PI), 0.0f, 1.0f, 0.0f);
    theView.layer.transform = rotationAndPerspectiveTransform;

    // Now need to get the top, left, width, height of the transformed view to correct the view's left offset

I have tried a number of ways of doing this by inspecting the CALayer, a failed attempt at using some matrix maths code snippets I found, but have not been able to crack it or even get close (depending on angle, a good 20 pixels out). Is there a way I can do this without spending 2 weeks reading a matrix maths textbook?

回答1:

The frame of a view is an axis-aligned rectangle in the superview's coordinate system. The frame fully encloses the view's bounds. If the view is transformed, the frame adjusts to tightly enclose the view's new bounds.

When you apply a Y-axis rotation and perspective to a view, the left and right edges of the view move toward its anchor point (which is normally the center of the view). The left edge also grows either taller or shorter, and the right edge does the opposite.

So the frame of the view (after applying the transformation) will give you the left edge coordinate and the width of the transformed view, and the top and height of the taller edge (which might be either the left or right edge). Here's my test code:

NSLog(@"frame before tilting = %@", NSStringFromCGRect(self.tiltView.frame));
float angle = 30.0;
CATransform3D rotationAndPerspectiveTransform = CATransform3DIdentity;
rotationAndPerspectiveTransform.m34 = -1.0 / 2000; // Perspective
rotationAndPerspectiveTransform = CATransform3DRotate(rotationAndPerspectiveTransform, 
       1 * angle / (180.0 / M_PI), 0.0f, 1.0f, 0.0f);
self.tiltView.layer.transform = rotationAndPerspectiveTransform;
NSLog(@"frame after tilting = %@", NSStringFromCGRect(self.tiltView.frame));

Here's the output:

2012-01-04 12:44:08.405 layer[72495:f803] frame before tilting = {{50, 50}, {220, 360}}
2012-01-04 12:44:08.406 layer[72495:f803] frame after tilting = {{62.0434, 44.91}, {190.67, 370.18}}

You can also get the coordinates of the corners of the view, in the superview's coordinate space using convertPoint:fromView: or convertPoint:toView:. Test code:

    CGRect bounds = self.tiltView.bounds;
    CGPoint upperLeft = bounds.origin;
    CGPoint upperRight = CGPointMake(CGRectGetMaxX(bounds), bounds.origin.y);
    CGPoint lowerLeft = CGPointMake(bounds.origin.x, CGRectGetMaxY(bounds));
    CGPoint lowerRight = CGPointMake(upperRight.x, lowerLeft.y);
#define LogPoint(P) NSLog(@"%s = %@ -> %@", #P, \
    NSStringFromCGPoint(P), \
    NSStringFromCGPoint([self.tiltView.superview convertPoint:P fromView:self.tiltView]))
    LogPoint(upperLeft);
    LogPoint(upperRight);
    LogPoint(lowerLeft);
    LogPoint(lowerRight);

Output:

2012-01-04 13:03:00.663 layer[72635:f803] upperLeft = {0, 0} -> {62.0434, 44.91}
2012-01-04 13:03:00.663 layer[72635:f803] upperRight = {220, 0} -> {252.713, 54.8175}
2012-01-04 13:03:00.663 layer[72635:f803] lowerLeft = {0, 360} -> {62.0434, 415.09}
2012-01-04 13:03:00.663 layer[72635:f803] lowerRight = {220, 360} -> {252.713, 405.182}

Notice that the Y coordinates of the upperLeft and upperRight points are different in the superview's coordinate system.