IOS Core Animation Advanced Techniques的学习笔记(四)

第五章: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

@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
- (void)viewDidLoad
{
    [super viewDidLoad];
	
    //rotate the layer 45 degrees
    CGAffineTransform transform = CGAffineTransformMakeScale(0.5, 0.5);
    self.layerView.layer.affineTransform = transform;
}


改动   CGAffineTransformMakeTranslation
- (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
- (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
@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);
代码:
@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(10,1100);




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)

sxX轴缩放,代表一个缩放比例,一般都是0 ---1之间的数字。

syY轴缩放。

sz:总体比例变换时,也就是m11(sx) == m22(sy)时。若m33(sz)>1,图形总体缩小。

        若< m33(sz) 1,图形总体放大,

        若m33(sz) 0。发生关于原点的对称等比变换。


sx = 1sy =1时。如图:


sx = 0.5sy =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

竖起来看相应前面的数据结构就非常明显了。


txX轴偏移位置。往下为正数。

tyY轴偏移位置。往右为正数。

tzZ轴偏移位置。往外为正数。



能够通过直接改动数据结构。来设置变换效果

struct CATransform3D

{

    CGFloat m11, m12, m13, m14

    CGFloat m21, m22, m23, m24

    CGFloat m31, m32, m33, m34

    CGFloat m41, m42, m43, m44

}

    CATransform3D transform = CATransform3DMakeRotation(M_PI_4, 0, 1, 0);
    transform.m11 = 2;

或者改动键值

    [myLayer setValue:[NSNumber numberWithInt:0] forKeyPath:@"transform.rotation.x"];




样例5.4
@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

#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

资源文件 front.png    back.png



Perspective Projection

前面提到过m34(透视效果,要操作的这个对象要有旋转的角度,否则没有效果。

正直/负值都有意义)


样例5.5
@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


假设改动凝视掉旋转,看看会有什么结果
    //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
@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文件,并改动代码
- (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°)
- (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设置
- (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






posted @ 2018-11-15 10:28  ldxsuanfa  阅读(198)  评论(0编辑  收藏  举报