接上文:iOS CALayer和3D (1): 定义一个简单的旋转3D立方体
在上篇文章中,我们使用CALayer创建了一个可以旋转的正方体,在这篇文章中,我们为程序加入触摸事件,使这个正方体可以在用户触摸下进行任意方向的旋转:
这里需要解决的问题是:CALayer的Transform实际上是对自身坐标系的Transform,但是我们需要旋转的方向和角度都是相对当前屏幕的坐标系,所以我们需要把针对当前屏幕坐标系的Transform转换成CALayer内部已经被变换坐标系的Transform。这会涉及到一些数学矩阵上的转换。因为3D变换是通过矩阵来实现的。
具体变换可以参考一篇非常棒的文章:How To Rotate a 3D Object Using Touches with OpenGL
虽然上面的文章使用的是OpenGL,但是由于3D变换都是用的矩阵,所以iOS中Core Animation的CATransform3D类型和GLKit中的GLKMatrix4类型也是通用的。所以我们可以通过OpenGL提供的矩阵变换方法来完成CALayer中任意方向触摸旋转的要求。
因此,首先引用GLKit.framework:
接着,加入GLKMath.h,因为只需要数学相关的矩阵变化函数,所以其实只需要GLKMath.h头文件:
#import <GLKit/GLKMath.h>
在上篇文章实现的代码基础上,删除后面的动画和添加_rootLayer时做的3D变换,仅仅设置CATransform3D的m34,然后设置_rootLayer的subLayerTransform属性,最后把_rootLayer加入到界面中:
//主Layer的3D变换
CATransform3D transform = CATransform3DIdentity;
transform.m34 = -1.0 / 700;
//设置CALayer的sublayerTransform
_rootLayer.sublayerTransform = transform;
//添加Layer
[self.view.layer addSublayer:_rootLayer];
这样,一个没有经过任何其他变换的静止正方体会显示在界面上。
然后,在UIViewController中改写UIResponder的touchesMoved方法。完成任意方向的旋转操作。
首先,需要把CATransform3D转换成GLKMatrix4,本以为可以直接转换,但是运行后出现BAD_ACCESS,所以使用GLKMatrix4MakeWithArray方法进行转换,需要把CATransform3D的指针传进去。接着使用上面文章中的方法,计算出屏幕触摸的变化并同时对GLKMatrix4进行相应的变换。最后,再把GLKMatrix4转换成CATransform3D结构体,并再次设置_rootLayer的sublayerTransform属性。
如下代码:
//改写UIViewController的touchesMoved方法(UIViewController继承也自UIResponder)
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
//把CATransform3D转换成GLKMatrix4
//直接用指针转换的话会BAD_ACCESS,所以使用GLKMatrix4MakeWithArray方法
CATransform3D caTransform3d = _rootLayer.sublayerTransform;
GLKMatrix4 _rotMatrix = GLKMatrix4MakeWithArray((void*)&caTransform3d);
//--- 来自http://www.raywenderlich.com/12667的代码 ---
UITouch * touch = [touches anyObject];
CGPoint location = [touch locationInView:self.view];
CGPoint lastLoc = [touch previousLocationInView:self.view];
CGPoint diff = CGPointMake(lastLoc.x - location.x, lastLoc.y - location.y);
//把原来的-1改成1,因为坐标系的Y轴是反转的
float rotX = 1 * GLKMathDegreesToRadians(diff.y / 2.0);
float rotY = -1 * GLKMathDegreesToRadians(diff.x / 2.0);
bool isInvertible;
GLKVector3 xAxis = GLKMatrix4MultiplyVector3(GLKMatrix4Invert(_rotMatrix, &isInvertible),
GLKVector3Make(1, 0, 0));
_rotMatrix = GLKMatrix4Rotate(_rotMatrix, rotX, xAxis.x, xAxis.y, xAxis.z);
GLKVector3 yAxis = GLKMatrix4MultiplyVector3(GLKMatrix4Invert(_rotMatrix, &isInvertible),
GLKVector3Make(0, 1, 0));
_rotMatrix = GLKMatrix4Rotate(_rotMatrix, rotY, yAxis.x, yAxis.y, yAxis.z);
//---
//把GLKMatrix4转换成CATransform3D,并设置CALayer的sublayerTransform
_rootLayer.sublayerTransform = *((CATransform3D*)&_rotMatrix);
}
再次运行程序,OK。