5變換
5.1 仿射變換
實(shí)際上UIView的transform屬性是一個(gè)CGAffineTransform類型,用于在二維空間做旋轉(zhuǎn)影钉,縮放和平移画髓。CGAffineTransform是一個(gè)可以和二維空間向量(例如CGPoint)做乘法的3X2的矩陣
當(dāng)對(duì)圖層應(yīng)用變換矩陣,圖層矩形內(nèi)的每一個(gè)點(diǎn)都被相應(yīng)地做變換平委,從而形成一個(gè)新的四邊形的形狀雀扶。CGAffineTransform中的“仿射”的意思是無(wú)論變換矩陣用什么值,圖層中平行的兩條線在變換之后任然保持平行肆汹,CGAffineTransform可以做出任意符合上述標(biāo)注的變換愚墓,
創(chuàng)建一個(gè)CGAffineTransform
Core Graphics提供了一系列函數(shù),對(duì)完全沒(méi)有數(shù)學(xué)基礎(chǔ)的開(kāi)發(fā)者也能夠簡(jiǎn)單地做一些變換昂勉。如下幾個(gè)函數(shù)都創(chuàng)建了一個(gè)CGAffineTransform實(shí)例:
CGAffineTransformMakeRotation(CGFloat angle)
CGAffineTransformMakeScale(CGFloat sx, CGFloat sy)
CGAffineTransformMakeTranslation(CGFloat tx, CGFloat ty)
旋轉(zhuǎn)和縮放變換都可以很好解釋--分別旋轉(zhuǎn)或者縮放一個(gè)向量的值浪册。平移變換是指每個(gè)點(diǎn)都移動(dòng)了向量指定的x或者y值--所以如果向量代表了一個(gè)點(diǎn),那它就平移了這個(gè)點(diǎn)的距離岗照。
demo如下:
@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
注意我們使用的旋轉(zhuǎn)常量是M_PI_4村象,而不是你想象的45,因?yàn)閕OS的變換函數(shù)使用弧度而不是角度作為單位攒至『裾撸弧度用數(shù)學(xué)常量pi的倍數(shù)表示,一個(gè)pi代表180度迫吐,所以四分之一的pi就是45度库菲。
#define RADIANS_TO_DEGREES(x) ((x)/M_PI*180.0)
混合變換
Core Graphics提供了一系列的函數(shù)可以在一個(gè)變換的基礎(chǔ)上做更深層次的變換,如果做一個(gè)既要縮放又要旋轉(zhuǎn)的變換志膀,這就會(huì)非常有用了熙宇。例如下面幾個(gè)函數(shù):
CGAffineTransformRotate(CGAffineTransform t, CGFloat angle)
CGAffineTransformScale(CGAffineTransform t, CGFloat sx, CGFloat sy)
CGAffineTransformTranslate(CGAffineTransform t, CGFloat tx, CGFloat ty)
當(dāng)操縱一個(gè)變換的時(shí)候,初始生成一個(gè)什么都不做的變換很重要--也就是創(chuàng)建一個(gè)CGAffineTransform類型的空值溉浙,矩陣論中稱作單位矩陣烫止,Core Graphics同樣也提供了一個(gè)方便的常量:
CGAffineTransformIdentity
最后,如果需要混合兩個(gè)已經(jīng)存在的變換矩陣戳稽,就可以使用如下方法馆蠕,在兩個(gè)變換的基礎(chǔ)上創(chuàng)建一個(gè)新的變換:
CGAffineTransformConcat(CGAffineTransform t1, CGAffineTransform t2);
我們來(lái)用這些函數(shù)組合一個(gè)更加復(fù)雜的變換,先縮小50%惊奇,再旋轉(zhuǎn)30度互躬,最后向右移動(dòng)200個(gè)像素。
- (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;
}
運(yùn)行完之后你會(huì)發(fā)現(xiàn)
圖片向右邊發(fā)生了平移赊时,但并沒(méi)有指定距離那么遠(yuǎn)(200像素)吨铸,另外它還有點(diǎn)向下發(fā)生了平移。原因在于當(dāng)你按順序做了變換祖秒,上一個(gè)變換的結(jié)果將會(huì)影響之后的變換诞吱,所以200像素的向右平移同樣也被旋轉(zhuǎn)了30度,縮小了50%竭缝,所以它實(shí)際上是斜向移動(dòng)了100像素房维。
這意味著變換的順序會(huì)影響最終的結(jié)果,也就是說(shuō)旋轉(zhuǎn)之后的平移和平移之后的旋轉(zhuǎn)結(jié)果可能不同抬纸。
5.2 3D變換
CG的前綴告訴我們咙俩,CGAffineTransform類型屬于Core Graphics框架,Core Graphics實(shí)際上是一個(gè)嚴(yán)格意義上的2D繪圖API,并且CGAffineTransform僅僅對(duì)2D變換有效阿趁。
和CGAffineTransform矩陣類似膜蛔,Core Animation提供了一系列的方法用來(lái)創(chuàng)建和組合CATransform3D
類型的矩陣,和Core Graphics的函數(shù)類似脖阵,但是3D的平移和旋轉(zhuǎn)多處了一個(gè)z參數(shù)皂股,并且旋轉(zhuǎn)函數(shù)除了angle之外多出了x,y,z三個(gè)參數(shù),分別決定了每個(gè)坐標(biāo)軸方向上的旋轉(zhuǎn):
CATransform3DMakeRotation(CGFloat angle, CGFloat x, CGFloat y, CGFloat z)
CATransform3DMakeScale(CGFloat sx, CGFloat sy, CGFloat sz)
CATransform3DMakeTranslation(Gloat tx, CGFloat ty, CGFloat tz)
代碼使用了CATransform3DMakeRotation對(duì)視圖內(nèi)的圖層繞Y軸做了45度角的旋轉(zhuǎn)命黔,我們可以把視圖向右傾斜呜呐,
@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
透視投影
CATransform3D的透視效果通過(guò)一個(gè)矩陣中一個(gè)很簡(jiǎn)單的元素來(lái)控制:m34。
@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
滅點(diǎn)
Core Animation定義了這個(gè)點(diǎn)位于變換圖層的anchorPoint(通常位于圖層中心悍募,但也有例外)蘑辑。這就是說(shuō),當(dāng)圖層發(fā)生變換時(shí)坠宴,這個(gè)點(diǎn)永遠(yuǎn)位于圖層變換之前anchorPoint的位置洋魂。
當(dāng)改變一個(gè)圖層的position,你也改變了它的滅點(diǎn)啄踊,做3D變換的時(shí)候要時(shí)刻記住這一點(diǎn)忧设,當(dāng)你視圖通過(guò)調(diào)m34來(lái)讓它更加有3D效果,應(yīng)該首先把它放置于屏幕中央颠通,然后通過(guò)平移來(lái)把它移動(dòng)到指定位置(而不是直接改變它的position)址晕,這樣所有的3D圖層都共享一個(gè)滅點(diǎn)。
sublayerTransform屬性
如果有多個(gè)視圖或者圖層顿锰,每個(gè)都做3D變換谨垃,那就需要分別設(shè)置相同的m34值,并且確保在變換之前都在屏幕中央共享同一個(gè)position硼控,如果用一個(gè)函數(shù)封裝這些操作的確會(huì)更加方便刘陶,但仍然有限制(例如,你不能在Interface Builder中擺放視圖)牢撼,這里有一個(gè)更好的方法匙隔。
CALayer有一個(gè)屬性叫做sublayerTransform。它也是CATransform3D類型熏版,但和對(duì)一個(gè)圖層的變換不同纷责,它影響到所有的子圖層。這意味著你可以一次性對(duì)包含這些圖層的容器做變換撼短,于是所有的子圖層都自動(dòng)繼承了這個(gè)變換方法再膳。
相較而言,通過(guò)在一個(gè)地方設(shè)置透視變換會(huì)很方便曲横,同時(shí)它會(huì)帶來(lái)另一個(gè)顯著的優(yōu)勢(shì):滅點(diǎn)被設(shè)置在容器圖層的中點(diǎn)喂柒,從而不需要再對(duì)子圖層分別設(shè)置了。這意味著你可以隨意使用position和frame來(lái)放置子圖層,而不需要把它們放置在屏幕中點(diǎn)灾杰,然后為了保證統(tǒng)一的滅點(diǎn)用變換來(lái)做平移蚊丐。
@property (weak, nonatomic) IBOutlet UIView *contintView1;
@property (weak, nonatomic) IBOutlet UIView *subView1;
@property (weak, nonatomic) IBOutlet UIView *subView2;
- (void)viewDidLoad
{
UIImage *img = [UIImage imageNamed:@"001"];
CATransform3D transtrom = CATransform3DIdentity;
transtrom.m34 = -1.0f / 500.0f;
self.contintView1.layer.sublayerTransform = transtrom;
self.subView1.layer.contents = (id)img.CGImage;
self.subView1.layer.transform = CATransform3DMakeRotation(M_PI_4, 0, 1, 0);
self.subView2.layer.contents = (id)img.CGImage;
self.subView2.layer.transform = CATransform3DMakeRotation(-M_PI_4, 0, 1, 0);
}
背面
將一個(gè)圖片按照y軸旋轉(zhuǎn)180度后,就會(huì)看到他的背面吭露。
self.subView1.layer.transform = CATransform3DMakeRotation(M_PI+M_PI_4, 0, 1, 0);
CATransform3D trans3d = CATransform3DIdentity;
trans3d = CATransform3DRotate(trans3d, M_PI, 0, 1, 0);
CALayer有一個(gè)叫做doubleSided的屬性來(lái)控制圖層的背面是否要被繪制吠撮。這是一個(gè)BOOL類型,默認(rèn)為YES讲竿,如果設(shè)置為NO,那么當(dāng)圖層正面從相機(jī)視角消失的時(shí)候弄屡,它將不會(huì)被繪制题禀。
self.imgVIew2.layer.doubleSided = YES;
如果設(shè)置為no 那么圖片旋轉(zhuǎn)180,是看不見(jiàn)任何東西的膀捷。
扁平化圖層
如果對(duì)包含已經(jīng)做過(guò)變換的圖層的圖層做反方向的變換將會(huì)發(fā)什么什么呢迈嘹?是不是有點(diǎn)困惑?