如何在透视视图中围绕其中心旋转平面对象?

tob*_*jim 30 catransform3d ios

我想要做的似乎应该很容易:我创建了一个旧的留声机唱片的2D自上而下的视图.我想将它旋转(放回)在X轴上然后围绕它的Z轴旋转.

我已经阅读了身上有CATransform3D的每个问题,我读过Steve Baker的"Matrices可能是你的朋友"一文以及Bill Dudney的书"Mac OS X和iPhone的核心动画"我认为Brad Larson的"没有轨迹球的3-D旋转"具有所有正确的代码,但由于他允许用户调整所有三个轴,我很难将他的代码缩小到我认为只是一个维度(旋转的z轴) ).

这是我正在测试的图像,而不是细节对问题很重要:

黄金记录

我以通常的方式在屏幕上显示:(在UIView的子类中)

- (void)awakeFromNib
{
    UIImage *recordImage = [UIImage imageNamed:@"3DgoldRecord"];
    if (recordImage) {
        recordLayer = [CALayer layer];
        [recordLayer setFrame:CGRectMake(0.0, 0.0, 1024, 1024)];
        [recordLayer setContents:(id)[[UIImage imageNamed:@"3DgoldRecord"] CGImage]];
        [self.layer addSublayer:recordLayer];
    }
}
Run Code Online (Sandbox Code Playgroud)

这是第一次测试,只是把它放在屏幕上;-)

然后,为了"放回"我应用一个变换来围绕图层的X轴旋转,在将图层的内容设置到图像之后和添加子图层之前插入此代码:

    CATransform3D myRotationTransform = 
                    CATransform3DRotate(recordLayer.transform,
                                        (M_PI_2 * 0.85), //experiment with flatness
                                        1.0, // rotate only across the x-axis
                                        0.0, // no y-axis transform
                                        0.0); //  no z-axis transform
    recordLayer.transform = myRotationTransform;        
Run Code Online (Sandbox Code Playgroud)

这符合预期:记录正在顺利回归.

并且为了下一步,导致记录旋转,我将此动画绑定到touchesEnded事件,尽管一旦退出测试/学习阶段,此旋转将不受用户控制:

CATransform3D currentTransform = recordLayer.transform; // to come back to at the end
CATransform3D myRotationTransform = 
                CATransform3DRotate(currentTransform,
                                    1.0, // go all the way around once
                                    (M_PI_2 * 0.85),    // x-axis change
                                    1.00,    // y-axis change ?
                                    0.0);   // z-axis change ?
recordLayer.transform = myRotationTransform;        
CABasicAnimation *myAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];
myAnimation.duration = 5.0;
myAnimation.fromValue = [NSNumber numberWithFloat:0.0];
myAnimation.toValue = [NSNumber numberWithFloat:M_PI * 2.0];
myAnimation.delegate = self;
[recordLayer addAnimation:myAnimation forKey:@"transform.rotation"];
Run Code Online (Sandbox Code Playgroud)

所以我敢肯定什么,我挂了是在CATransform3DRotate调用(相信我的矢量:现在什么上市还有我一直在矢量简单的变化看变化......简直是最后的变化我试过了).据我了解,变换中x,y和z的值实质上是动画期间传入的值的百分比,范围从fromValue到toValue.

如果我在正确的轨道上理解这一点,是否有可能在一次转换中表达这一点?或者,对于每个有效的动画帧,我必须围绕z轴稍微旋转原始竖直图像,然后沿x轴旋转放下结果吗?我看到了一个关于组合变换的问题/答案,但这是一个缩放变换,然后是旋转变换.我已经搞砸了转换变换,但是没有做我认为我应该得到的(即将一个变换的结果传递给下一个的变换参数似乎只是完全执行一个然后动画另一个).

rob*_*off 63

如果您使用a CATransformLayer作为图像视图图层的父级,这是最简单的.因此,您需要一个CATransformLayer用作其图层的自定义视图子类.这是微不足道的:

@interface TransformView : UIView

@end

@implementation TransformView

+ (Class)layerClass {
    return [CATransformLayer class];
}

@end
Run Code Online (Sandbox Code Playgroud)

放入TransformView你的笔尖,并添加一个UIImageView作为子视图TransformView.将这些视图连接到视图控制器中名为transformView和的插座discView.

在视图控制器中,设置transformView应用透视的变换(通过设置m34)和X轴倾斜:

- (void)viewDidLoad
{
    [super viewDidLoad];
    CATransform3D transform = CATransform3DIdentity;
    transform.m34 = -1 / 500.0;
    transform = CATransform3DRotate(transform, .85 * M_PI_2, 1, 0, 0);
    self.transformView.layer.transform = transform;
}
Run Code Online (Sandbox Code Playgroud)

关键路径动画添加transform.rotation.zdiscViewviewWillAppear:和删除viewDidDisappear::

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];

    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
    animation.fromValue = [NSNumber numberWithFloat:0];
    animation.toValue = [NSNumber numberWithFloat:2 * M_PI];
    animation.duration = 1.0;
    animation.repeatCount = HUGE_VALF;
    animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
    [self.discView.layer addAnimation:animation forKey:@"transform.rotation.z"];
}

- (void)viewDidDisappear:(BOOL)animated {
    [super viewDidDisappear:animated];
    [self.discView.layer removeAllAnimations];
}
Run Code Online (Sandbox Code Playgroud)

结果:

旋转盘

UPDATE

这是一个Swift游乐场演示:

import UIKit
import XCPlayground

class TransformView: UIView {
    override class func layerClass() -> AnyClass {
        return CATransformLayer.self
    }
}

let view = UIView(frame: CGRectMake(0, 0, 300, 150))
view.backgroundColor = UIColor.groupTableViewBackgroundColor()
XCPlaygroundPage.currentPage.liveView = view

let transformView = TransformView(frame: view.bounds)
view.addSubview(transformView)

var transform = CATransform3DIdentity
transform.m34 = CGFloat(-1) / transformView.bounds.width
transform = CATransform3DRotate(transform, 0.85 * CGFloat(M_PI_2), 1, 0, 0)
transformView.layer.transform = transform

let image = UIImage(named: "3DgoldRecord")!
let imageView = UIImageView(image: image)
imageView.bounds = CGRectMake(0, 0, 200, 200)
imageView.center = CGPointMake(transformView.bounds.midX, transformView.bounds.midY)
transformView.addSubview(imageView)

let animation = CABasicAnimation(keyPath: "transform.rotation.z")
animation.fromValue = 0
animation.toValue = 2 * M_PI
animation.duration = 1
animation.repeatCount = Float.infinity
animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
imageView.layer.addAnimation(animation, forKey: animation.keyPath)
Run Code Online (Sandbox Code Playgroud)

将问题中的图像复制到游乐场资源文件夹中并命名3DgoldRecord.png.结果:

游乐场结果

  • 我对使用OpenGL重写它不感兴趣. (22认同)