Chapter 1
</br>
Core Animation不是一個嚴(yán)謹(jǐn)?shù)目蚣堋?code>CAAnimation才是一個框架名,它是QuartzCore
的一部分袖迎。
Core Animation是用C寫的坛缕,所以要注意類型轉(zhuǎn)換猫牡,用“無縫橋接” __bridge
胡诗。
</br>
UIView
是視圖,CALayer
是圖層淌友。
</br>
UIView
和 CALayer
的關(guān)系:
UIView
處理用戶交互煌恢,CALayer
不管。每個
UIView
都有一個CALayer
實例的圖層屬性亩进,不能在Interface Builder中設(shè)置UIView
的layer
屬性症虑。
</br>
處理視圖比單獨處理圖層更方便。
Chapter 2
</br>
知識點:
-
CALayer
的contents
屬性 -
UIView
的contentMode
屬性和CALayer
的contentsGravity
屬性 -
CALayer
的contentsScale
屬性 -
UIView
的clipsToBounds
屬性和CALayer
的maskToBounds
屬性 -
CALayer
的contentsRect
屬性 -
CALayer
的contentsCenter
屬性(可在Interface Builder
中設(shè)置) -
CALayer
的delegate
屬性归薛,遵循CALayerDelegate
-
CALayer
的-displayer
方法
</br>
寄宿圖(圖層中包含的圖):
CALayer
的contents
屬性
UIImage *image = [UIImage imageNamed:@"picture"];
self.layerView.layer.contents = (__bridge id)(image.CGImage);
這樣的寄宿圖會在圖層中被拉伸以適應(yīng)視圖的大小谍憔,可以加上下面這句,讓顯示的圖片適應(yīng)原來的比例
self.layerView.contentMode = UIViewContentModeScaleAspectFit;
由此可見主籍,UIView
大多數(shù)視覺相關(guān)的屬性比如上面用的contentMode
习贫,對這些屬性的操作其實是對對應(yīng)圖層的操作。
CALayer
的contentMode
屬性叫contentsGravity
self.layerView.layer.contentsGravity = kCAGravityResizeAspect;
可以達(dá)到像剛才設(shè)置UIView
的contentMode
屬性同樣的效果千元。
注意防止用錯誤的contentsScale
去顯示Retina圖片苫昌。CGImage
沒有UIImage
中的拉伸概念。當(dāng)用UIImage
讀取的圖片都是Retina圖片幸海,在UIImage
轉(zhuǎn)換為CGImage
時祟身,拉伸的元素將會丟失奥务。contentsScale
默認(rèn)值為1.0
,如果把contentsGravity
屬性設(shè)為kCAGravityCenter
袜硫,不設(shè)置contentsScale
(即采用默認(rèn)值)氯葬,就會出現(xiàn)這種錯誤現(xiàn)實Retina圖片的情況。
正確做法:
UIImage *image = [UIImage imageNamed:@"picture"];
self.layerView.layer.contents = (__bridge id)(image.CGImage);
self.layerView.layer.contentsGravity = kCAGravityCenter;
self.layerView.layer.contentsScale = image.scale;
默認(rèn)情況下婉陷,UIView
和CALayer
都會繪制超過邊界的內(nèi)容或子視圖帚称。
只要設(shè)置UIView
的屬性clipsToBounds
或CALayer
的maskToBounds
設(shè)置為YES
。就不會超出邊界了秽澳。
self.layerView.clipsToBounds = YES;
//或
self.layerView.layer.maskToBounds = YES;
除了可以使用CALayer
的contents
屬性設(shè)置寄宿圖闯睹,還可以-drawRect
方法自定義繪圖,此方法沒有默認(rèn)的實現(xiàn)担神,因為UIView不一定有寄宿圖楼吃,如果不需要寄宿圖,就不要實現(xiàn)此方法杏瞻,否則會浪費資源所刀。
CALayer
要顯式調(diào)用-display
方法,當(dāng)圖層顯示在屏幕上時捞挥,CALayer
不會自動重繪它的內(nèi)容。它把重繪的決定權(quán)交給了開發(fā)者忧吟。
沒有用masksToBounds
屬性砌函,繪制的那個圓仍然沿邊界被裁剪了。這是因為使用CALayerDelegate
繪制寄宿圖的時候溜族,并沒有對超出邊界外的內(nèi)容提供繪制支持讹俊。
除非你創(chuàng)建了一個單獨的圖層,你幾乎沒有機(jī)會用到CALayerDelegate協(xié)議煌抒。因為當(dāng)UIView創(chuàng)建了它的宿主圖層時仍劈,它就會自動地把圖層的delegate設(shè)置為它自己,并提供了一個-displayLayer:的實現(xiàn)寡壮,那所有的問題就都沒了贩疙。
但是,當(dāng)使用寄宿了視圖的圖層的時候况既,也不必實現(xiàn)-displayLayer:
和-drawLayer:inContext:
方法來繪制你的寄宿圖这溅。通常做法是實現(xiàn)UIView
的-drawRect:
方法,UIView
就會幫你做完剩下的工作棒仍,包括在需要重繪的時候調(diào)用-display
方法悲靴。
</br>
Chapter 3
</br>
知識點:
-
UIView
的frame
、bounds
莫其、center
屬性和CALayer
的frame
癞尚、bounds
耸三、position
屬性 -
CALayer
的anchorPoint
屬性 -
CALayer
的zPosition
屬性 -
CALayer
的-containsPoint:
方法和-hitTest:
方法
布局
UIView
三個重要布局屬性:frame
、bounds
浇揩、center
對應(yīng)CALayer
的frame
仪壮、bounds
、position
临燃。
frame
代表了圖層的外部坐標(biāo)(也就是在父圖層上占據(jù)的空間)睛驳,bounds
是內(nèi)部坐標(biāo)({0, 0}通常是圖層的左上角),center
和position
都代表了相對于父圖anchorPoint
所在的位置膜廊。
視圖的frame
乏沸,bounds
和center
屬性僅僅是存取方法,當(dāng)操縱視圖的frame
爪瓜,實際上是在改變位于視圖下方CALayer
的frame
蹬跃,不能夠獨立于圖層之外改變視圖的frame
。對于視圖或者圖層來說铆铆,frame
并不是一個非常清晰的屬性蝶缀,它其實是一個虛擬屬性,是根據(jù)bounds
薄货,position
和transform
計算而來翁都,所以當(dāng)其中任何一個值發(fā)生改變,frame
都會變化谅猾。相反柄慰,改變frame
的值同樣會影響到他們當(dāng)中的值。
當(dāng)對圖層做變換的時候税娜,比如旋轉(zhuǎn)或者縮放坐搔,
frame
實際上代表了覆蓋在圖層旋轉(zhuǎn)之后的整個軸對齊的矩形區(qū)域,也就是說
frame
的寬高可能和bounds
的寬高不再一致了敬矩。根據(jù)上圖可以看到這點概行。
錨點
視圖的center
屬性和圖層的position
屬性都指定了anchorPoint
相對于父圖層的位置。圖層的anchorPoint
通過position
來控制它的frame
的位置弧岳,你可以認(rèn)為anchorPoint
是用來移動圖層的把柄凳忙。默認(rèn)來說,anchorPoint
位于圖層的中點缩筛,所以圖層的將會以這個點為中心放置消略。anchorPoint
屬性并沒有被UIView
接口暴露出來嗡贺,這也是視圖的position
屬性被叫做“center”
的原因愉棱。但是圖層的anchorPoint
可以被移動费什,比如你可以把它置于圖層frame
的左上角傍药,于是圖層的內(nèi)容將會向右下角的position
方向移動汉操,而不是居中了嫂冻。
注意到创坞,當(dāng)改變了
anchorPoint
府寒,position
屬性保持固定的值并沒有發(fā)生改變,但是frame
卻移動了伤提。
和contentsRect
與contentsCenter
屬性類似巫俺,anchorPoint
用單位坐標(biāo)來描述,也就是圖層的相對坐標(biāo)肿男,圖層左上角是{0, 0}
介汹,右下角是{1, 1}
,因此默認(rèn)坐標(biāo)是{0.5, 0.5}
舶沛。anchorPoint
可以通過指定x
和y
值小于0
或者大于1
嘹承,使它放置在圖層范圍之外。
鐘擺例子使用anchorPoint
:
給各時針修改錨點:
self.hourHand.layer.anchorPoint = CGPointMake(0.5f, 0.9f);
self.minuteHand.layer.anchorPoint = CGPointMake(0.5f, 0.9f);
self.secondHand.layer.anchorPoint = CGPointMake(0.5f, 0.9f);
</br>
坐標(biāo)系
和視圖一樣如庭,圖層在圖層樹當(dāng)中也是相對于父圖層按層級關(guān)系放置叹卷,一個圖層的position
依賴于它父圖層的bounds
,如果父圖層發(fā)生了移動坪它,它的所有子圖層也會跟著移動骤竹。
這樣對于放置圖層會更加方便,因為你可以通過移動根圖層來將它的子圖層作為一個整體來移動往毡,但是有時候你需要知道一個圖層的絕對位置蒙揣,或者是相對于另一個圖層的位置,而不是它當(dāng)前父圖層的位置开瞭。CALayer
給不同坐標(biāo)系之間的圖層轉(zhuǎn)換提供了一些工具類方法:
-(CGPoint)convertPoint:(CGPoint)point fromLayer:(CALayer *)layer
-(CGPoint)convertPoint:(CGPoint)point toLayer:(CALayer *)layer
-(CGRect)convertRect:(CGRect)rect fromLayer:(CALayer *)layer
-(CGRect)convertRect:(CGRect)rect toLayer:(CALayer *)layer
這些方法可以把定義在一個圖層坐標(biāo)系下的點或者矩形轉(zhuǎn)換成另一個圖層坐標(biāo)系下的點或者矩形鸣奔。
Z坐標(biāo)軸
和UIView
嚴(yán)格的二維坐標(biāo)系不同,CALayer
存在于一個三維空間當(dāng)中惩阶。除了我們已經(jīng)討論過的position
和anchorPoint
屬性之外,CALayer
還有另外兩個屬性扣汪,zPosition
和anchorPointZ
断楷,二者都是在Z軸上描述圖層位置的浮點類型。
zPosition
屬性在大多數(shù)情況下其實并不常用崭别。在第五章冬筒,我們將會涉及CATransform3D
,你會知道如何在三維空間移動和旋轉(zhuǎn)圖層茅主,除了做變換之外舞痰,zPosition
最實用的功能就是改變圖層的顯示順序了。
通常诀姚,圖層是根據(jù)它們子圖層的sublayers
出現(xiàn)的順序來類繪制的响牛,這就是所謂的畫家的算法--就像一個畫家在墻上作畫--后被繪制上的圖層將會遮蓋住之前的圖層,但是通過增加圖層的zPosition
,就可以把圖層向相機(jī)方向前置呀打,于是它就在所有其他圖層的前面了(或者至少是小于它的zPosition
值的圖層的前面)矢赁。
為greenView圖層設(shè)置
zPosition
屬性:
self.greenView.layer.zPosition = 1.0f;
Hit Testing
最好使用圖層相關(guān)視圖撩银,而不是創(chuàng)建獨立的圖層關(guān)系。其中一個原因就是要處理額外復(fù)雜的觸摸事件豺憔。
CALayer
并不關(guān)心任何響應(yīng)鏈?zhǔn)录罨瘢圆荒苤苯犹幚碛|摸事件或者手勢。但是它有一系列的方法幫你處理事件:-containsPoint:
和-hitTest:
恭应。
-containsPoint:
接受一個在本圖層坐標(biāo)系下的CGPoint
抄邀,如果這個點在圖層frame
范圍內(nèi)就返回YES
。下面看例:
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIView *layerView;
@property (strong, nonatomic) CALayer *blueLayer;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.blueLayer = [CALayer layer];
self.blueLayer.frame = CGRectMake(50.0f, 50.0f, 100.0f, 100.0f);
self.blueLayer.backgroundColor = [UIColor blueColor].CGColor;
[self.layerView.layer addSublayer:self.blueLayer];
}
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
CGPoint point = [[touches anyObject] locationInView:self.view];
point = [self.layerView.layer convertPoint:point fromLayer:self.view.layer];
if ([self.layerView.layer containsPoint:point]) {
point = [self.blueLayer convertPoint:point fromLayer:self.layerView.layer];
if ([self.blueLayer containsPoint:point]) {
NSLog(@"Inside Blue Layer.");
} else {
NSLog(@"Inside White Layer.");
}
}
}
當(dāng)點擊白色視圖圖層的子圖層時暮屡,打印
Inside Blue Layer
撤摸,點擊白色視圖圖層時,打印Inside White Layer
褒纲。
-hitTest:
方法同樣接受一個CGPoint
類型參數(shù)准夷,它返回圖層本身,或者包含這個坐標(biāo)點的葉子節(jié)點圖層莺掠。這意味著不再需要像使用-containsPoint:
那樣衫嵌,人工地在每個子圖層變換或者測試點擊的坐標(biāo)。如果這個點在最外面圖層的范圍之外彻秆,則返回nil
楔绞。具體使用-hitTest:
方法被點擊圖層的代碼如下:
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
CGPoint point = [[touches anyObject] locationInView:self.view];
CALayer *layer = [self.layerView.layer hitTest:point];
if (layer == self.blueLayer) {
NSLog(@"Inside Blue Layer");
} else if (layer == self.layerView.layer) {
NSLog(@"Inside White Layer");
}
}
注意當(dāng)調(diào)用圖層的
-hitTest:
方法時,測算的順序嚴(yán)格依賴于圖層樹當(dāng)中的圖層順序(和UIView
處理事件類似)唇兑。之前提到的zPosition
屬性可以明顯改變屏幕上圖層的順序酒朵,但不能改變事件傳遞的順序。
這意味著如果改變了圖層的z軸順序扎附,你會發(fā)現(xiàn)將不能夠檢測到最前方的視圖點擊事件蔫耽,這是因為被另一個圖層遮蓋住了,雖然它的zPosition
值較小留夜,但是在圖層樹中的順序靠前匙铡。
</br>
Chapter 4
</br>
知識點:
-
CALayer
的cornerRadius
屬性 -
CALayer
的borderWidth
屬性 -
CALayer
的borderColor
屬性 -
CALayer
的shadowOpacity
屬性 -
CALayer
的shadowColor
屬性 -
CALayer
的shadowOffset
屬性 -
CALayer
的shadowRadius
屬性 -
CALayer
的mask
屬性 -
CALayer
的magnificationFilter
屬性和minificationFilter
屬性 -
CALayer
的shouldRasterize
屬性 -
CALayer
的rasterizationScale
屬性
</br>
制造矩形圓角,用CALayer
的cornerRadius
屬性碍粥,CGFloat
類型鳖眼,默認(rèn)值為0(直角)。
borderWidth
屬性用來設(shè)置圖層的邊界寬度嚼摩,CGFloat
類型钦讳。borderColor
屬性就是設(shè)置圖層的顏色矿瘦,CGColorRef
類型,默認(rèn)為黑色蜂厅。如果圖層的子圖層超過了邊界匪凡,或者是寄宿圖在透明區(qū)域有一個透明蒙板,邊框仍然會沿著圖層的邊界繪制出來掘猿。
陰影:shadowOpacity
屬性取值在0.0(不可見)到1.0(完全不透明)之間病游,CGFloat
類型〕硗ǎ可以改變?nèi)齻€相關(guān)屬性改變陰影的表現(xiàn):shadowColor
衬衬,shadowOffset
,shadowRadius
改橘。
shadowOffset
是CGSize
滋尉,寬度控制橫向位移,高度控制縱向位移飞主,其默認(rèn)值為{0狮惜, -3}
(陰影向上)。
shadowRadius
控制陰影的模糊度碌识,CGFloat
類型碾篡。當(dāng)它值為0(默認(rèn)值)時,陰影就有一個和視圖一樣有一個明確地邊界值筏餐,值越大邊界越模糊开泽,越接近自然陰影的效果。
和圖層邊框不同魁瞪,圖層的陰影繼承自內(nèi)容的外形穆律,而不是根據(jù)邊界和角半徑來確定。為了計算出陰影的形狀导俘,Core Animation
會將寄宿圖(包括子視圖峦耘,如果有的話)考慮在內(nèi),然后通過這些來完美搭配圖層形狀從而創(chuàng)建一個陰影旅薄。
但是CALayer
的maskToBounds
屬性會把陰影也去掉:
如果想沿著內(nèi)容裁切,你需要用到兩個圖層:一個只畫陰影的空的外圖層赋秀,和一個用masksToBounds
裁剪內(nèi)容的內(nèi)圖層。
我們已經(jīng)知道圖層陰影并不總是方的律想,而是從圖層內(nèi)容的形狀繼承而來猎莲。這看上去不錯,但是實時計算陰影也是一個非常消耗資源的技即,尤其是圖層有多個子圖層著洼,每個圖層還有一個有透明效果的寄宿圖的時候。
因此如果事先知道陰影形狀,可以使用shadowPath
提高性能身笤,其類型為CGPathRef
豹悬。
CGPath
是一個Core Graphics
對象,用來指定任意的一個矢量圖形液荸。我們可以通過這個屬性單獨于圖層形狀之外指定陰影的形狀瞻佛。
//enable layer shadows
self.layerView1.layer.shadowOpacity = 0.5f;
self.layerView2.layer.shadowOpacity = 0.5f;
//create a square shadow CGMutablePathRef
squarePath = CGPathCreateMutable();
CGPathAddRect(squarePath, NULL, self.layerView1.bounds);
self.layerView1.layer.shadowPath = squarePath;
//create a circular shadow
CGMutablePathRef circlePath = CGPathCreateMutable();
CGPathAddEllipseInRect(circlePath, NULL, self.layerView2.bounds);
self.layerView2.layer.shadowPath = circlePath;
如果是一個矩形或者是圓,用
CGPath
會相當(dāng)簡單明了娇钱。但是如果是更加復(fù)雜一點的圖形伤柄,UIBezierPath
類會更合適,它是一個由UIKit
提供的在CGPath
基礎(chǔ)上的Objective-C包裝類文搂。
圖層蒙板
CALayer
的mask
屬性适刀,其類型也是CALayer
。有和其他圖層一樣的繪制和布局屬性煤蹭。它類似于一個子圖層笔喉,相對于父圖層布局,但是它卻不是一個普通的子圖層硝皂。不同于那些繪制在父圖層中的子圖層常挚,mask圖層定義了父圖層的部分可見區(qū)域。如果mask圖層比父圖層要小吧彪,只有在mask圖層里面的內(nèi)容才是它關(guān)心的待侵,除此以外的一切都會被隱藏起來。
下面用一個UIImageView
(UIView
的子類)演示一下:
UIImage *viewImage = [UIImage imageNamed:@"Igloo"];
//set mask
CALayer *maskLayer = [CALayer layer];
maskLayer.frame = self.imageView.bounds;
UIImage *maskImage = [UIImage imageNamed:@"Cone.png"];
maskLayer.contents = (__bridge id)maskImage.CGImage;
self.imageView.image = viewImage;
self.imageView.layer.mask = maskLayer;
CALayer
蒙板圖層真正厲害的地方在于蒙板圖不局限于靜態(tài)圖姨裸。任何有圖層構(gòu)成的都可以作為mask
屬性秧倾,這意味著你的蒙板可以通過代碼甚至是動畫實時生成。
</br>
拉伸過濾
</br>
總得來講傀缩,當(dāng)我們視圖顯示一個圖片的時候那先,都應(yīng)該正確地顯示這個圖片(意即:以正確的比例和正確的1:1像素顯示在屏幕上)赡艰。原因如下:
- 能夠顯示最好的畫質(zhì)慷垮,像素既沒有被壓縮也沒有被拉伸揖闸。
- 能更好的使用內(nèi)存,因為這就是所有你要存儲的東西汤纸。
- 最好的性能表現(xiàn)芹血,CPU不需要為此額外的計算贮泞。
不過有時候楞慈,顯示一個非真實大小的圖片確實是我們需要的效果囊蓝。比如說一個頭像或是圖片的縮略圖令蛉,再比如說一個可以被拖拽和伸縮的大圖言询。這些情況下,為同一圖片的不同大小存儲不同的圖片顯得又不切實際夫啊。
當(dāng)圖片需要顯示不同的大小的時候撇眯,有一種叫做拉伸過濾
的算法就起到作用了虱咧。它作用于原圖的像素上并根據(jù)需要生成新的像素顯示在屏幕上腕巡。
重繪圖片大小沒有一個統(tǒng)一的通用算法绘沉。這取決于需要拉伸的內(nèi)容,放大或是縮小的需求等這些因素车伞。CALayer
為此提供了三種拉伸過濾方法:
(1)kCAFilterLinear
(2)kCAFilterNearest
(3)kCAFilterTrilinear
minification
(縮小圖片)和magnification
(放大圖片)默認(rèn)的過濾器都是kCAFilterLinear
,這個過濾器采用雙線性濾波算法困曙,它在大多數(shù)情況下都表現(xiàn)良好慷丽。雙線性濾波算法通過對多個像素取樣最終生成新的值盈魁,得到一個平滑的表現(xiàn)不錯的拉伸窃诉。但是當(dāng)放大倍數(shù)比較大的時候圖片就模糊不清了飘痛。
kCAFilterTrilinear
和kCAFilterLinear
非常相似宣脉,大部分情況下二者都看不出來有什么差別。但是塑猖,較雙線性濾波算法而言羊苟,三線性濾波算法存儲了多個大小情況下的圖片(也叫多重貼圖)蜡励,并三維取樣,同時結(jié)合大圖和小圖的存儲進(jìn)而得到最后的結(jié)果兼都。
這個方法的好處在于算法能夠從一系列已經(jīng)接近于最終大小的圖片中得到想要的結(jié)果扮碧,也就是說不要對很多像素同步取樣慎王。這不僅提高了性能柬祠,也避免了小概率因舍入錯誤引起的取樣失靈的問題负芋。對于大圖來說旧蛾,雙線性濾波和三線性濾波表現(xiàn)得更出色锨天。
kCAFilterNearest
是一種比較武斷的方法病袄。從名字不難看出赘阀,這個算法(也叫最近過濾)就是取樣最近的單像素點而不管其他的顏色基公。這樣做非澈涠梗快酸休,也不會使圖片模糊祷杈。但是吠式,最明顯的效果就是特占,會使得壓縮圖片更糟是目,圖片放大之后也顯得塊狀或是馬賽克嚴(yán)重。對于沒有斜線的小圖來說揉抵,最近過濾算法要好很多冤今。
總的來說,對于比較小的圖或者是差異特別明顯脚囊,極少斜線的大圖悔耘,最近過濾算法會保留這種差異明顯的特質(zhì)以呈現(xiàn)更好的結(jié)果。但是對于大多數(shù)的圖尤其是有很多斜線或是曲線輪廓的圖片來說校摩,最近過濾算法會導(dǎo)致更差的結(jié)果秧耗。換句話說,線性過濾保留了形狀霉猛,最近過濾則保留了像素的差異珠闰。
拉伸實驗伏嗜,使用一個1x的圖片顯示:
</br>
iOS常見的做法是把一個空間的alpha
值設(shè)置為0.5
(50%)以使其看上去呈現(xiàn)為不可用狀態(tài)裸影。對于獨立的視圖來說還不錯轩猩,但是當(dāng)一個控件有子視圖的時候就有點奇怪了均践,例如一個內(nèi)嵌了UILabel的自定義UIButton:
左邊是一個不透明的按鈕彤委,右邊是50%透明度的相同按鈕焦影。我們可以注意到薇宠,里面的標(biāo)簽的輪廓跟按鈕的背景很不搭調(diào)澄港。
這是由透明度的混合疊加造成的回梧,當(dāng)你顯示一個50%透明度的圖層時,圖層的每個像素都會一般顯示自己的顏色拯欧,另一半顯示圖層下面的顏色镐作。這是正常的透明度的表現(xiàn)隆箩。但是如果圖層包含一個同樣顯示50%透明的子圖層時捌臊,你所看到的視圖理澎,50%來自子視圖糠爬,25%來自圖層本身的顏色秩铆,另外的25%則來自背景色。
可以設(shè)置CALayer
的一個叫做shouldRasterize
屬性來實現(xiàn)組透明的效果捅膘,如果它被設(shè)置為YES
寻仗,在應(yīng)用透明度之前署尤,圖層及其子圖層都會被整合成一個整體的圖片曹体,這樣就沒有透明度混合的問題了箕别。
為了啟用shouldRasterize
屬性串稀,我們設(shè)置了圖層的rasterizationScale
屬性母截。默認(rèn)情況下清寇,所有圖層拉伸都是1.0
, 所以如果你使用了shouldRasterize
屬性陷遮,你就要確保你設(shè)置了rasterizationScale
屬性去匹配屏幕,以防止出現(xiàn)Retina屏幕像素化的問題比吭。
</br>
Chapter 5
</br>
第三章中UIView
的transform
屬性是一個CGAffineTransform
類型衩藤,用于在二維空間做旋轉(zhuǎn)赏表,縮放和平移瓢剿。CGAffineTransform
是一個可以和二維空間向量(例如CGPoint
)做乘法的3X2的矩陣:
式中用
CGPoint
的每一列和CGAffineTransform
矩陣的每一行對應(yīng)元素相乘再求和间狂,就形成了一個新的CGPoint
類型的結(jié)果鉴象。要解釋一下圖中顯示的灰色元素纺弊,為了能讓矩陣做乘法淆游,左邊矩陣的列數(shù)一定要和右邊矩陣的行數(shù)個數(shù)相同稽犁,所以要給矩陣填充一些標(biāo)志值已亥,使得既可以讓矩陣做乘法虑椎,又不改變運算結(jié)果捆姜,并且沒必要存儲這些添加的值泥技,因為它們的值不會發(fā)生變化珊豹,但是要用來做運算店茶。
因此贩幻,通常會用3×3
(而不是2×3
)的矩陣來做二維變換丛楚,你可能會見到3行2列格式的矩陣趣些,這是所謂的以列為主的格式喧务,上圖所示的是以行為主的格式,只要能保持一致庐冯,用哪種格式都無所謂展父。
當(dāng)對圖層應(yīng)用變換矩陣栖茉,圖層矩形內(nèi)的每一個點都被相應(yīng)地做變換吕漂,從而形成一個新的四邊形的形狀惶凝。CGAffineTransform
中的“仿射”的意思是無論變換矩陣用什么值苍鲜,圖層中平行的兩條線在變換之后任然保持平行混滔,CGAffineTransform
可以做出任意符合上述標(biāo)注的變換坯屿,?下圖顯示了一些仿射的和非仿射的變換:
UIView
可以通過設(shè)置transform
屬性做變換,但實際上它只是封裝了內(nèi)部圖層的變換隔节。
CALayer
同樣也有一個transform
屬性怎诫,但它的類型是CATransform3D
幻妓,而不是CGAffineTransform
肉津,本章后續(xù)將會詳細(xì)解釋妹沙。CALayer
對應(yīng)于UIView
的transform
屬性叫做affineTransform
距糖。
</br>
旋轉(zhuǎn)
</br>
使用CALayer
的affineTransform
屬性把一個圖片旋轉(zhuǎn)45°:
CGAffineTransform transform = CGAffineTransformMakeRotation(M_PI_4);
self.layerView.layer.affineTransform = transform;
注意我們使用的旋轉(zhuǎn)常量是
M_PI_4
恩脂,而不是你想象的45
俩块,因為iOS的變換函數(shù)使用弧度而不是角度作為單位典阵∽嘲。弧度用數(shù)學(xué)常量pi
的倍數(shù)表示歹啼,一個pi
代表180°
狸眼,所以四分之一的pi就是45°
拓萌。C的數(shù)學(xué)函數(shù)庫(iOS會自動引入)提供了
pi
的一些簡便的換算,M_PI_4
于是就是pi
的四分之一
</br>
混合變換
</br>
Core Graphics
提供了一系列的函數(shù)可以在一個變換的基礎(chǔ)上做更深層次的變換
CGAffineTransformRotate(CGAffineTransform t, CGFloat angle)
CGAffineTransformScale(CGAffineTransform t, CGFloat sx, CGFloat sy)
CGAffineTransformTranslate(CGAffineTransform t, CGFloat tx, CGFloat ty)
當(dāng)操縱一個變換的時候炕倘,初始生成一個什么都不做的變換很重要--也就是創(chuàng)建一個CGAffineTransform
類型的空值罩旋,矩陣論中稱作單位矩陣涨醋,Core Graphics
同樣也提供了一個方便的常量:
CGAffineTransformIdentity
最后东帅,如果需要混合兩個已經(jīng)存在的變換矩陣靠闭,就可以使用如下方法愧膀,在兩個變換的基礎(chǔ)上創(chuàng)建一個新的變換:
CGAffineTransformConcat(CGAffineTransform t1, CGAffineTransform t2);