第五章:Transforms
Affine Transforms
CGAffineTransform是二维的
Creating a CGAffineTransform
主要有三种变化方法
旋转:
CGAffineTransformMakeRotation(CGFloat angle)
缩放:
CGAffineTransformMakeScale(CGFloat sx, CGFloat sy)
移动:
CGAffineTransformMakeTranslation(CGFloat tx, CGFloat ty)
例子5.1 CGAffineTransformMakeRotation
源码在这里下载:http://www.informit.com/title/9780133440751
[objc] view plaincopyprint?
- @interface ViewController ()
- @property (nonatomic, weak) IBOutlet UIView *layerView;
- @end
- @implementation ViewController
- - (void)viewDidLoad
- {
- [super viewDidLoad];
- //rotate the layer 45 degrees
- CGAffineTransform transform = CGAffineTransformMakeRotation(M_PI_4);
- self.layerView.layer.affineTransform = transform;
- }
- @end
修改 CGAffineTransformMakeScale
[objc] view plaincopyprint?
- - (void)viewDidLoad
- {
- [super viewDidLoad];
- //rotate the layer 45 degrees
- CGAffineTransform transform = CGAffineTransformMakeScale(0.5, 0.5);
- self.layerView.layer.affineTransform = transform;
- }
修改 CGAffineTransformMakeTranslation
[objc] view plaincopyprint?
- - (void)viewDidLoad
- {
- [super viewDidLoad];
- //rotate the layer 45 degrees
- CGAffineTransform transform = CGAffineTransformMakeTranslation(-50.0, 30.0);
- self.layerView.layer.affineTransform = transform;
- }
Combining Transforms
方法1:使用CGAffineTransformConcat
继续修改例子5.1
[objc] view plaincopyprint?
- - (void)viewDidLoad
- {
- [super viewDidLoad];
- //rotate the layer 45 degrees
- CGAffineTransform transform1 = CGAffineTransformMakeRotation(M_PI_4);
- CGAffineTransform transform2 = CGAffineTransformMakeScale(0.5, 0.5);
- CGAffineTransform transform = CGAffineTransformConcat(transform1, transform2);
- self.layerView.layer.affineTransform = transform;
- }
方法2:
CGAffineTransformRotate(CGAffineTransform t, CGFloat angle)
CGAffineTransformScale(CGAffineTransform t, CGFloat sx, CGFloat sy)
CGAffineTransformTranslate(CGAffineTransform t, CGFloat tx, CGFloat ty)
和前面的CGAffineTransformMakeRotation函数相同,也可以混用
CGAffineTransform t可以使用CGAffineTransformIdentity函数初始化
例子5.2
[objc] view plaincopyprint?
- @interface ViewController ()
- @property (nonatomic, weak) IBOutlet UIView *layerView;
- @end
- @implementation ViewController
- - (void)viewDidLoad
- {
- [super viewDidLoad];
- //create a new transform
- CGAffineTransform transform = CGAffineTransformIdentity;
- //scale by 50%
- transform = CGAffineTransformScale(transform, 0.5, 0.5);
- //rotate by 30 degrees
- transform = CGAffineTransformRotate(transform, M_PI / 180.0 * 30.0);
- //translate by 200 points
- transform = CGAffineTransformTranslate(transform, 200, 0);
- //apply transform to layer
- self.layerView.layer.affineTransform = transform;
- }
- @end
下面记几个特殊值
1. 首先要知道函数 CGAffineTransformIdentity 初始化的结果
2. 左右翻转
CGAffineTransformMake(-1,0,0,1,0,0);
3. 以右边为轴向右翻转
CGAffineTransformMake(-1,0,0,1,self.layerView.frame.size.width,0);
4. 上下翻转
CGAffineTransformMake(1,0,0, -1,0,0);
5. 以底边为轴向下翻转
CGAffineTransformMake(1,0,0, -1,0,self.layerView.frame.size.height);
6. 转180°
CGAffineTransformMake(-1,0,0, -1,0,0);
7. 例子5.3,向右斜拉
CGAffineTransformMake(1,0, -1,1,0,0);
代码:
[objc] view plaincopyprint?
- @interface ViewController ()
- @property (nonatomic, weak) IBOutlet UIView *layerView;
- @end
- @implementation ViewController
- CGAffineTransform CGAffineTransformMakeShear(CGFloat x, CGFloat y)
- {
- CGAffineTransform transform = CGAffineTransformIdentity;
- transform.c = -x;
- transform.b = y;
- return transform;
- }
- - (void)viewDidLoad
- {
- [super viewDidLoad];
- //shear the layer at a 45-degree angle
- self.layerView.layer.affineTransform = CGAffineTransformMakeShear(1, 0);
- }
- @end
8. 例子5.3,向左斜拉
CGAffineTransformMake(1, 0,1, 1, 0, 0);
3D Transforms
类似CGAffineTransform,CATransform3D是三维的
CATransform3D又是一个结构。他有自己的一个公式,可以进行套用。
struct CATransform3D
{
CGFloat m11(x缩放), m12(y切变), m13(旋转), m14( );
CGFloat m21(x切变), m22(y缩放), m23( ), m24( );
CGFloat m31(旋转), m32( ), m33( ), m34(透视效果,要操作的这个对象要有旋转的角度,否则没有效果。正直/负值都有意义);
CGFloat m41(x平移), m42(y平移), m43(z平移), m44( );
};
同样有三种变换方法
旋转:
CATransform3DMakeRotation(CGFloat angle, CGFloat x, CGFloat y, CGFloat z)
首先要先清楚x,y,z是什么
{x, y, z}组成的向量就是旋转要使用的轴,angle是旋转角度
例:原图
向X轴旋转45度。 向Y轴旋转45度。 向Z轴旋转45度。
向 X轴,Y轴都旋转45度,就是沿着对角线旋转。
缩放:
CATransform3DMakeScale(CGFloat sx, CGFloat sy, CGFloat sz)
sx:X轴缩放,代表一个缩放比例,一般都是0 ---1之间的数字。
sy:Y轴缩放。
sz:整体比例变换时,也就是m11(sx) == m22(sy)时,若m33(sz)>1,图形整体缩小,
若0 < m33(sz) < 1,图形整体放大,
若m33(sz) < 0,发生关于原点的对称等比变换。
当sx = 1,sy =1时。如图:
当sx = 0.5,sy =0.5时。如图:
变换:
CATransform3DMakeTranslation(Gloat tx, CGFloat ty, CGFloat tz)
t‘ = [1 0 0 0; 0 1 0 0; 0 0 1 0; tx ty tz 1]
1 0 0 0
0 1 0 0
0 0 1 0
tx ty tz 1
竖起来看对应前面的数据结构就很明显了。
tx:X轴偏移位置,往下为正数。
ty:Y轴偏移位置,往右为正数。
tz:Z轴偏移位置,往外为正数。
可以通过直接修改数据结构,来设置变换效果
struct CATransform3D
{
CGFloat m11, m12, m13, m14;
CGFloat m21, m22, m23, m24;
CGFloat m31, m32, m33, m34;
CGFloat m41, m42, m43, m44;
}
[objc] view plaincopyprint?
- CATransform3D transform = CATransform3DMakeRotation(M_PI_4, 0, 1, 0);
- transform.m11 = 2;
或者修改键值
[objc] view plaincopyprint?
- [myLayer setValue:[NSNumber numberWithInt:0] forKeyPath:@"transform.rotation.x"];
例子5.4
[objc] view plaincopyprint?
- @interface ViewController ()
- @property (nonatomic, weak) IBOutlet UIView *layerView;
- @end
- @implementation ViewController
- - (void)viewDidLoad
- {
- [super viewDidLoad];
- //rotate the layer 45 degrees along the Y axis
- CATransform3D transform = CATransform3DMakeRotation(M_PI_4, 0, 1, 0);
- self.layerView.layer.transform = transform;
- }
- @end
修改例子5.4,修改自http://lepetit-prince.net/ios/?p=451
[objc] view plaincopyprint?
- #import "ViewController.h"
- #import <QuartzCore/QuartzCore.h>
- @interface ViewController ()
- {
- BOOL front;
- }
- @property (nonatomic, weak) IBOutlet UIView *layerView;
- @end
- @implementation ViewController
- - (void)viewDidLoad
- {
- [super viewDidLoad];
- front = YES;
- self.layerView.layer.contents = (__bridge id)([UIImage imageNamed:@"front.png"].CGImage);
- }
- - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
- {
- [UIView animateWithDuration:0.5 animations:^{
- self.layerView.layer.transform = CATransform3DMakeRotation(M_PI * 0.5, 0.0f, 1.0f, 0.0f);
- } completion:^(BOOL finished) {
- self.layerView.layer.transform = CATransform3DMakeRotation(M_PI * 1.5, 0.0f, 1.0f, 0.0f);
- self.layerView.layer.contents = front ? (__bridge id)([UIImage imageNamed:@"back.png"].CGImage) : (__bridge id)([UIImage imageNamed:@"front.png"].CGImage);
- [UIView animateWithDuration:0.5 animations:^{
- self.layerView.layer.transform = CATransform3DMakeRotation(M_PI * 2, 0.0f, 1.0f, 0.0f);
- } completion:^(BOOL finished) {
- front = !front;
- }];
- }];
- }
- @end
Perspective Projection
前面提到过m34(透视效果,要操作的这个对象要有旋转的角度,否则没有效果。正直/负值都有意义)
例子5.5
[objc] view plaincopyprint?
- @interface ViewController ()
- @property (nonatomic, weak) IBOutlet UIView *layerView;
- @end
- @implementation ViewController
- - (void)viewDidLoad
- {
- [super viewDidLoad];
- //create a new transform
- CATransform3D transform = CATransform3DIdentity;
- //apply perspective
- transform.m34 = - 1.0 / 500.0;
- //rotate by 45 degrees along the Y axis
- transform = CATransform3DRotate(transform, M_PI_4, 0, 1, 0);
- //apply to layer
- self.layerView.layer.transform = transform;
- }
- @end
如果修改注释掉旋转,看看会有什么结果
[objc] view plaincopyprint?
- //rotate by 45 degrees along the Y axis
- //transform = CATransform3DRotate(transform, M_PI_4, 0, 1, 0);
例子是用的透视场景是±1.0/d,d镜头到景物的距离,取值500~1000效果最好,±代表方向
The Vanishing Point
当景物慢慢远离镜头时,随着越来越小最终聚集到一点就是Vanishing Point(灭点)
通常情况灭点是在视图的正中心,或者在包含所有景物范围的中心。
Core Animation把灭点定义在anchorPoint,所以在变换前需要确定anchorPoint,
尤其需要注意,3D变换时最好确保同一视图内的所有layey有相同的灭点
The sublayerTransform Property
如果你有多个View或Layer有相同的3D变换,就可以使用sublayerTransform,
sublayerTransform也是CATransform3D,只有sublayers才会响应。
默认值是Identity Transform(CATransform3DIdentity)
例子5.6
[objc] view plaincopyprint?
- @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];
- //apply perspective transform to container
- CATransform3D perspective = CATransform3DIdentity;
- perspective.m34 = - 1.0 / 500.0;
- self.containerView.layer.sublayerTransform = perspective;
- //rotate layerView1 by 45 degrees along the Y axis
- CATransform3D transform1 = CATransform3DMakeRotation(M_PI_4, 0, 1, 0);
- self.layerView1.layer.transform = transform1;
- //rotate layerView2 by 45 degrees along the Y axis
- CATransform3D transform2 = CATransform3DMakeRotation(-M_PI_4, 0, 1, 0);
- self.layerView2.layer.transform = transform2;
- }
- @end
我们挪动一下xib里的图片位置:
再看结果
恢复xib文件,并修改代码
[objc] view plaincopyprint?
- - (void)viewDidLoad
- {
- [super viewDidLoad];
- //apply perspective transform to container
- // CATransform3D perspective = CATransform3DIdentity;
- // perspective.m34 = - 1.0 / 500.0;
- // self.containerView.layer.sublayerTransform = perspective;
- //apply perspective
- CATransform3D transform1 = CATransform3DIdentity;
- transform1.m34 = - 1.0 / 500.0;
- transform1 = CATransform3DRotate(transform1, M_PI_4, 0, 1, 0);
- self.layerView1.layer.transform = transform1;
- //rotate layerView2 by 45 degrees along the Y axis
- CATransform3D transform2 = CATransform3DIdentity;
- transform2.m34 = - 1.0 / 500.0;
- transform2 = CATransform3DRotate(transform2, -M_PI_4, 0, 1, 0);
- self.layerView2.layer.transform = transform2;
- }
结果和最初一样,但再次修改xib文件,挪动图片看结果,比较未改代码时的效果
大家发现设置sublayerTransform的好处了吗
1. 可以一次设置所有subLayer的变换效果
2. Vanishing Point(灭点)被同时设置在container layer即父图层的中心,这就意味着无论你怎么
修改subLayer的position或frame,它们都会保持一个相同的灭点。
Backfaces
例子5.4,我们设置的是旋转M_PI_4(45°),改为M_PI(180°)
[objc] view plaincopyprint?
- - (void)viewDidLoad
- {
- [super viewDidLoad];
- //rotate the layer 45 degrees along the Y axis
- CATransform3D transform = CATransform3DMakeRotation(M_PI, 0, 1, 0);
- self.layerView.layer.transform = transform;
- }
翻到了layer背面,显示的是原图像的镜像图。由此可见layer是双面的,并且两面都被描绘了。
因此我们会想到,为什么要浪费GPU去描绘我们看不见的部分呢。CALayer的另外一个属性
doubleSided可以解决这个问题。
在刚才修改过的例子5.4的代码中增加doubleSided设置
[objc] view plaincopyprint?
- - (void)viewDidLoad
- {
- [super viewDidLoad];
- //rotate the layer 45 degrees along the Y axis
- CATransform3D transform = CATransform3DMakeRotation(M_PI, 0, 1, 0);
- self.layerView.layer.transform = transform;
- self.layerView.layer.doubleSided = NO;
- }
图像没有了
Layer Flattening
自己看例子吧,例子5.7和5.8
Solid Objects
也自己看例子吧,例子5.9和5.10