iOS Core Animation Advanced Techniques學習筆記(2)

變換

仿射變換

CGAffineTransform是一個可以和二維空間向量(如CGPoint)做乘法的3*2的矩陣。當對圖層應用變換矩陣,圖層內的每一個點都被相應地做變換,從而形成一個新的四邊形的形狀。CGAffineTransform中仿射的意思是無論變換矩陣用什么值,圖層中平行的兩條線在變換后任然保持平行。

UIView可以通過設置transform屬性做變換,但實際上它只是封裝了內部圖層的變換。CALayer同樣也有一個transform屬性,但它的類型是CATransform3D,而不是CGAffineTransform。CALayer對應于UIView的transform屬性叫做affineTransform

CGAffineTransformMakeRotation(CGFloat angle)              // 旋轉
CGAffineTransformMakeScale(CGFloat sx, CGFloat sy)        // 縮放
CGAffineTransformMakeTranslation(CGFloat tx, CGFloat ty)  // 平移

由于iOS變換函數使用弧度而不是角度作為單位,所以做旋轉變換的時候可以使用如下宏來將角度換算成弧度:

#define RADIANS_TO_DEGREES(x) ((x)/M_PI*180.0)

混合變換

混合變換函數:

CGAffineTransformRotate(CGAffineTransform t, CGFloat angle)
CGAffineTransformScale(CGAffineTransform t, CGFloat sx, CGFloat sy)
CGAffineTransformTranslate(CGAffineTransform t, CGFloat tx, CGFloat ty)
CGAffineTransformConcat(CGAffineTransform t1, CGAffineTransform t2)

當生成一個混合變換的時候,首先需要創建一個CGAffineTransform類型的空值,矩陣論中稱為單位矩陣,Core Graphics 中提供了一個方便的常量:

CGAffineTransformIdentity

如果需要混合兩個已經存在的變換矩陣,就可以使用如下方法,在兩個變換的基礎上創建一個新的變換:

CGAffineTransformConcat(CGAffineTransform t1, CGAffineTransform t2);

示例代碼:

- (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;
}

3D變換

CATransform3D是一個可以在3維空間內做變換的4*4的矩陣。和CGAffineTransform矩陣類似,Core Animation提供了一系列的方法用來創建和組合CATransform3D類型的矩陣,和Core Graphics的函數類似,但是3D的平移和旋轉多處了一個z參數,并且旋轉函數除了angle之外多出了x,y,z三個參數,分別決定了每個坐標軸方向上的旋轉:

CATransform3DMakeRotation(CGFloat angle, CGFloat x, CGFloat y, CGFloat z)
CATransform3DMakeScale(CGFloat sx, CGFloat sy, CGFloat sz) 
CATransform3DMakeTranslation(Gloat tx, CGFloat ty, CGFloat tz)

繞Z軸的旋轉等同于之前二維空間的仿射旋轉,但是繞X軸和Y軸的旋轉就突破了屏幕的二維空間,并且在用戶視角看來發生了傾斜。

如果要實現透視效果,還需要引入投影變換(又稱作z變換)來對除了旋轉之外的變換矩陣做一些修改,而這可以通過修改矩陣值來實現。CATransform3D中的透視效果通過矩陣中一個很簡單的元素來控制:m34m34用于按比例縮放x和y的值來計算到底要離視角多遠。

m34的默認值是0,可以通過設置m34為-1.0/d來應用透視效果。d代表了想象中視角相機和屏幕之間的距離,以像素為單位,通常設置為500-1000。

@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;
}

滅點

滅點是指在透視角度物體遠離視角的那端匯聚消失的那個點。在現實中,這個點通常是視圖的中心,于是為了在屏幕中創建擬真效果的透視,這個點應該聚在屏幕中點,或者至少是包含所有3D對象的視圖中點。

Core Animation定義了這個點位于變換圖層的anchorPoint。這就是說,當圖層發生變換時,這個點永遠位于圖層變換之前anchorPoint的位置。

當改變一個圖層的position,也就改變了它的滅點,做3D變換的時候要時刻記住這一點,當視圖通過調整m34來讓它更加有3D效果,應該首先把它放置于屏幕中央,然后通過平移來把它移動到指定位置,而不是直接改變它的position,這樣所有的3D圖層都共享一個滅點。

sublayerTransform屬性

如果要為多個視圖或圖層做3D變換并且保證滅點設置在容器圖層中心,可以使用CALayer的sublayerTransform屬性:

@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;
}

禁用背面繪制:

layer.doubleSided = NO;

固體對象

示例代碼:

@implementation RootViewController

- (void)viewDidLoad {
    
    [super viewDidLoad];
    
    CATransform3D perspective = CATransform3DIdentity;
    perspective.m34 = -1.0 / 500;
    perspective = CATransform3DRotate(perspective, -M_PI_4, 1, 0, 0);
    perspective = CATransform3DRotate(perspective, -M_PI, 0, 1, 0);
    
    self.containerView.layer.sublayerTransform = perspective;
    
    CATransform3D transform = CATransform3DMakeTranslation(0, 0, 100);
    [self addFace:0 withTransform:transform];
    
    transform = CATransform3DMakeTranslation(100, 0, 0);
    transform = CATransform3DRotate(transform, M_PI_2, 0, 1, 0);
    [self addFace:1 withTransform:transform];
    
    transform = CATransform3DMakeTranslation(-100, 0, 0);
    transform = CATransform3DRotate(transform, -M_PI_2, 0, 1, 0);
    [self addFace:2 withTransform:transform];
    
    transform = CATransform3DMakeTranslation(0, 100, 0);
    transform = CATransform3DRotate(transform, -M_PI_2, 1, 0, 0);
    [self addFace:3 withTransform:transform];
    
    transform = CATransform3DMakeTranslation(0, -100, 0);
    transform = CATransform3DRotate(transform, M_PI_2, 1, 0, 0);
    [self addFace:4 withTransform:transform];
    
    transform = CATransform3DMakeTranslation(0, 0, -100);
    transform = CATransform3DRotate(transform, M_PI, 0, 1, 0);
    [self addFace:5 withTransform:transform];
}

- (void)addFace:(NSInteger)index withTransform:(CATransform3D)transfrom {

    UIView *view = self.faces[index];
    
    [self.containerView addSubview:view];
    
    view.center = self.containerView.center;
    
    view.layer.transform = transfrom;
    view.layer.borderWidth = 1.0f;
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容