5

Take a look at the following progression of images:

enter image description here enter image description here enter image description here

What's happening here is first I selected "Baseball", causing the 3 gray views you see to rotate left so that the one with the various levels is in the middle (second image). You can see that already at this point the view's width has been substantially reduced. The third image occurs after clicking on the view containing the sports names, causing it to rotate back to the middle.

From what I can tell, the width of a view is reduced when it rotates into the middle. But I have no idea why.

Here's the relevant code. pos refers to where the view in question is going to be located. 0 indicates that it's in the middle, 1 indicates that it is one to the right of the middle, -1 is one to the left of the middle, etc.

- (void)perspectiveView:(MenuSection *)view forPosition:(NSInteger)pos
{

    CALayer *layer = view.layer;
    CATransform3D transform = CATransform3DIdentity;

    MenuSection *prevView = (pos >= 0) ? (MenuSection *)[self viewWithTag:view.tag - 1] : (MenuSection *)[self viewWithTag:view.tag + 1];

    if (pos == 0) {
        view.changingToFrame = CGRectMake(round((480 - view.frame.size.width)/2), view.frame.origin.y, view.frame.size.width, view.frame.size.height);
        view.frame = view.changingToFrame;

    } else {

        if (pos == 1) {
            NSLog(@"%@",NSStringFromCGRect(prevView.changingToFrame));
            view.changingToFrame = CGRectMake(round(prevView.changingToFrame.origin.x + prevView.frame.size.width + 20), view.frame.origin.y, view.frame.size.width, view.frame.size.height);
            transform.m34 = 1.0 / 1000;
        } else if (pos == -1){
            view.changingToFrame = CGRectMake(round(prevView.changingToFrame.origin.x - view.frame.size.width - 20), view.frame.origin.y, view.frame.size.width, view.frame.size.height);
            transform.m34 = 1.0 / -1000;
        } else if (pos == 2){
            view.changingToFrame = CGRectMake(round(prevView.changingToFrame.origin.x + prevView.frame.size.width + 20), view.frame.origin.y, view.frame.size.width, view.frame.size.height);
            transform.m34 = 1.0 / 500;
        } else if (pos == -2){
            view.changingToFrame = CGRectMake(round(prevView.changingToFrame.origin.x - view.frame.size.width - 20), view.frame.origin.y, view.frame.size.width, view.frame.size.height);
            transform.m34 = 1.0 / -500;
        }

        view.frame = view.changingToFrame;

        transform = CATransform3DRotate(transform, 45.0f * M_PI / 180.0f, 0.0f, 1.0f, 0.0f);
    }

    layer.transform = transform;
}
maxedison
  • 17,243
  • 14
  • 67
  • 114
  • I solved this by adding a very slight amount of z axis transform. It doesn't always work, and if done wrong, will fold your view along the y=x axis. – CodaFi Jan 17 '12 at 04:42
  • What is the transform.m34 for? I've rotated views like this without the width ever changing. Not sure what's going on... – bentford Jan 19 '12 at 17:25
  • I got the transform code from this post: http://stackoverflow.com/questions/347721/how-do-i-apply-a-perspective-transform-to-a-uiview. Can you provide a code example for how you've rotated views? If I remove the transform.m34 statements, then I just end up with transforms that look like this: http://cl.ly/2Z0v0P1U1T2a1p240x3L – maxedison Jan 19 '12 at 17:43

2 Answers2

7

I've solved it. Turns out that applying CATransform3D actually changes the frame of the view. In the code I posted, when I set the new frame of the view (because it rotates into a different position), I simply use view.frame.size.width in CGRectMake() in an attempt to maintain the width. BUT, since the CATransform3D had previously modified the width, I don't get the actual width the I want.

The solution was to subclass the necessary UIViews and store their original frame in an ivar. That way, I can always use that as the reference point when moving the views to different positions.

maxedison
  • 17,243
  • 14
  • 67
  • 114
0

You shouldn't need to edit the CATransform3D matrices directly (m34, etc...) for this effect to work. Try this:

if (pos == 0) {
     view.changingToFrame = CGRectMake(round((480 - view.frame.size.width)/2), view.frame.origin.y, view.frame.size.width, view.frame.size.height);
    view.frame = view.changingToFrame;
} else {
    if (pos == 1) {
         view.changingToFrame = CGRectMake(round(prevView.changingToFrame.origin.x + prevView.frame.size.width + 20), view.frame.origin.y, view.frame.size.width, view.frame.size.height);
        transform = CATransform3DRotate(transform, M_PI/4.0f, 0.0f, 1.0f, 0.0f);
    } else if (pos == -1){
        view.changingToFrame = CGRectMake(round(prevView.changingToFrame.origin.x - view.frame.size.width - 20), view.frame.origin.y, view.frame.size.width, view.frame.size.height);
        transform = CATransform3DRotate(transform, -M_PI/4.0f, 0.0f, 1.0f, 0.0f);
    } else if (pos == 2){
        view.changingToFrame = CGRectMake(round(prevView.changingToFrame.origin.x + prevView.frame.size.width + 20), view.frame.origin.y, view.frame.size.width, view.frame.size.height);
        transform = CATransform3DRotate(transform, M_PI/3.0f, 0.0f, 1.0f, 0.0f);
    } else if (pos == -2){
        view.changingToFrame = CGRectMake(round(prevView.changingToFrame.origin.x - view.frame.size.width - 20), view.frame.origin.y, view.frame.size.width, view.frame.size.height);
        transform = CATransform3DRotate(transform, -M_PI/3.0f, 0.0f, 1.0f, 0.0f);
    }
    view.frame = view.changingToFrame;
}
layer.transform = transform;

and then play with those angles until it looks right.

  • You have to modify `m34` directly if you want perspective. See "Modifying the Transform Data Structure" in the *[Core Animation Programming Guide](http://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/CoreAnimation_guide/Articles/Layers.html#//apple_ref/doc/uid/TP40006082-SW14)*. – rob mayoff Jan 24 '12 at 19:48