《iOS核心动画高级技巧》笔记(三) - 变换
变换
- 研究可以用来对图层旋转,摆放或者扭曲的
CGAffineTransform
,以及可以将扁平物体转换成三维空间对象的CATransform3D
仿射变换
UIView
的transform
属性是一个CGAffineTransform
类型,用于在二维空间做旋转,缩放和平移,实际上它只是封装了内部图层的变换。CGAffineTransformMakeRotation(CGFloat angle)
CGAffineTransformMakeScale(CGFloat sx, CGFloat sy)
CGAffineTransformMakeTranslation(CGFloat tx, CGFloat ty)
* `CALayer`同样也有一个`transform`属性,但它的类型是`CATransform3D`,而不是`CGAffineTransform`。 * `CALayer`对应于`UIView`的`transform`属性是`affineTransform`. * 弧度换算:`#define DEGREES_TO_RADIANS(x) ((x)/180.0*M_PI)`, * `M_PI`:180(度),`M_PI_4`:180/4(度) ```swift @interface ViewController ()
@property (nonatomic, weak) IBOutlet UIView *layerView;
@end
@implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
CGAffineTransform transform = CGAffineTransformMakeRotation(M_PI_4);
self.layerView.layer.affineTransform = transform;
}
@end
#####混合变换 * 当操纵一个变换的时候,初始生成一个什么都不做的变换很重要 * `CGAffineTransformIdentity` * 混合两个已经存在的变换矩阵 * `CGAffineTransformConcat(CGAffineTransform t1, CGAffineTransform t2);` * 注意:**下一个变换是基于上一个变换的结果** ```swift - (void)viewDidLoad
{
[super viewDidLoad];
CGAffineTransform transform = CGAffineTransformIdentity;
transform = CGAffineTransformScale(transform, 0.5, 0.5);
transform = CGAffineTransformRotate(transform, M_PI / 180.0 * 30.0);
transform = CGAffineTransformTranslate(transform, 200, 0);
self.layerView.layer.affineTransform = transform;
}
####3D变换 * `CG`的前缀告诉我们,`CGAffineTransform`类型属于`Core Graphics`框架,`Core Graphics`实际上是一个严格意义上的`2D`绘图`API`,并且`CGAffineTransform`仅仅对`2D`变换有效。 ```swift CATransform3DMakeRotation(CGFloat angle, CGFloat x, CGFloat y, CGFloat z)
CATransform3DMakeScale(CGFloat sx, CGFloat sy, CGFloat sz)
CATransform3DMakeTranslation(Gloat tx, CGFloat ty, CGFloat tz)
* 简单运用。 ```swift @implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
CATransform3D transform = CATransform3DMakeRotation(M_PI_4, 0, 1, 0);
self.layerView.layer.transform = transform;
}
@end
Snip20170526_2.png
透视投影
CATransform3D
的透视效果通过一个矩阵中一个很简单的元素来控制:m34
m34
的默认值是 0,我们可以通过设置m34
为-1.0 / d
来应用透视效果,d
代表了想象中视角相机和屏幕之间的距离,以像素为单位,通常500-1000
就已经很好了@implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
CATransform3D transform = CATransform3DIdentity;
transform.m34 = - 1.0 / 500.0;
transform = CATransform3DRotate(transform, M_PI_4, 0, 1, 0);
self.layerView.layer.transform = transform;
}
@end
Snip20170526_3.png
灭点
- 当在透视角度绘图的时候,远离相机视角的物体将会变小变远,当远离到一个极限距离,它们 可能就缩成了一个点,于是所有的物体最后都汇聚消失在同一个点。
- 这个点位于变换图层的
anchorPoint
,改变一个图层的position
,你也改变了它的灭点- 这句话大家自己理解,为什么要共享一个灭点?我估计效果应该是让其他3D图层整体看起来更加协调一些。
- 当视图通过调整
m34
来让它更加有3D
效果,应该首先把它放置于屏幕中央,然后通过平移来把它移动到指定位置(而不是直接改变它的position
),这样所有的3D
图层都共享一个灭点。
sublayerTransform
属性
sublayerTransform
:CALayer
的属性,CATransform3D
类型,一次性对包含这些图层的容器做变换,所有的子图层都自动继承了这个变换方法.- 另一个显著的优势:灭点被设置在容器图层的中点,从而不需要再对子图层分别设置了。意味着你可以随意使用
position
和frame
来放置子图层,而不需要把它们放置在屏幕中点,然后为了保证统一的灭点用变换来做平移。@interface ViewController ()
@property (nonatomic, weak) IBOutlet UIView *containerView;
@property (nonatomic, weak) IBOutlet UIView *layerView1;
@property (nonatomic, weak) IBOutlet UIView *layerView2;
@end
@implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
CATransform3D perspective = CATransform3DIdentity;
perspective.m34 = - 1.0 / 500.0;
self.containerView.layer.sublayerTransform = perspective;
CATransform3D transform1 = CATransform3DMakeRotation(M_PI_4, 0, 1, 0);
self.layerView1.layer.transform = transform1;
CATransform3D transform2 = CATransform3DMakeRotation(-M_PI_4, 0, 1, 0);
self.layerView2.layer.transform = transform2;
}
Snip20170526_4.png
Snip20170526_5.png
- 个人理解:对于内部的图层并不直接是容器图层的子图层没有效果,如:内部图层的内部图层。
背面
- 图层是双面绘制的,反面显示的是正面的一个镜像图片。
doubleSided
:CALayer
的属性,控制图层的背面是否要被绘制。这是一个BOOL
类型,默认为YES
。- 如果设置为
NO
,那么当图层正面从相机视角消失的时候,它将不会被绘制。扁平化图层
- 如果对包含已经做过变换的图层的图层做反方向的变换是否会回复原样?
- 针对 Z 轴做平面旋转可以达到预期效果(白色内部的深灰色图层是反方向变换后的,下同)
Snip20170526_6.png
- 针对 Y 轴做立体旋转不能达到预期效果
Snip20170526_7.png
- 针对 Y 轴旋转预期效果
Snip20170526_8.png
- 由于它们并不都存在同一个3D空间。每个图层的3D场景其实是扁平化的,当你从正面观察一个图层,看到的实际上由子图层创建的想象出来的3D场景,但当你倾斜这个图层,你会发现实际上这个3D场景仅仅是被绘制在图层的表面。(
CATransformLayer
子类用于解决此类问题)- 个人理解:每个图层都有属于自己的3D空间,子图层3D效果是基于父图层的3D空间进行绘制的,所以父图层变换后,子图层变换是基于父图层。
固体对象(略)原文地址
大概内容就是通过代码用六个视图控件拼成一个立方体,导入
GLKit
库进行阴暗面处理,最后监听 3 上按钮的点击事件。