一、contents
CALayer的contents屬性,雖然被定義為id類型,貌似可以指向任何類型的對象,但是在實際中,如果給contents賦的不是CGImage,那么得到的layer顯示的將是空白。
之所以定義為id,是為了兼容Mac OS,在Mac OS上,該屬性對CGImage和NSImage都能生效。
二、contentsGravity
在使用UIImageView的時候,當我們加載的圖片與imageView的大小比例不一致時,圖片將會被拉伸。此時可以設置contentMode為UIViewContentModeScaleAspectFit。
同樣的,在CALayer中,也有類似屬性,即contentsGravity。該屬性是個NSString,可選值為
kCAGravityCenter
kCAGravityTop
kCAGravityBottom
kCAGravityLeft
kCAGravityRight
kCAGravityTopLeft
kCAGravityTopRight
kCAGravityBottomLeft
kCAGravityBottomRight
kCAGravityResize
kCAGravityResizeAspect
kCAGravityResizeAspectFill
具體作用自測哈~~
三、contentsScale
contentsScale定義了content對應的圖片的像素尺寸和視圖大小的比例,默認為1。該屬性屬于支持高分辨率(Retina)屏幕機制的一部分。它用來判斷在繪制圖層的時候應該為寄宿圖創(chuàng)建的空間大小,和需要顯示
的圖片的拉伸度(假設并沒有設置 contentsGravity 屬性)。UIView有一個類似功能但是非常少用到的 contentScaleFactor 屬性。
如果contentsScale設置為1,將會以每個點1個像素繪制圖片,如果設置為2,則會以每個點2個像素繪制圖片,也就是我們常說的Retina屏幕。
此外,該屬性與contentsGravity有時候會產(chǎn)生沖突,例如當我們設置contentsGravity為kCAGravityResizeAspect自動拉伸以適應圖層后,設置contentsScale無效。
當我們用UIImage加載圖片時,若圖片為@2x圖片,則UIImage的屬性scale為2.因此一般設置contentsScale都是根據(jù)content(CGImage)對應的UIImage的scale來設置。
四、contentsRect
好啦,終點來了,其實這個才是真正想說的。
CALayer的contentsRect屬性允許我們在contents中僅顯示原圖的一個子區(qū)域。
要注意,該屬性雖然是一個CGRect類型,但其單位并非我們所熟知的點。它使用的是單位坐標,指定在0-1之間,是一個相對值(相對原圖的寬高)。
附IOS用到的坐標系統(tǒng)簡介:
點——在iOS和MacOS中最常見的坐標體系。點就像是虛擬的像素,也被稱
作邏輯像素。在標準設備上,一個點就是一個像素,但是在Retina設備上,一
個點等于2*2個像素。iOS用點作為屏幕的坐標測算體系就是為了在Retina設備
和普通設備上能有一致的視覺效果。
像素——物理像素坐標并不會用來屏幕布局,但是仍然與圖片有相對關(guān)系。
UIImage是一個屏幕分辨率解決方案,所以指定點來度量大小。但是一些底層
的圖片表示如CGImage就會使用像素,所以你要清楚在Retina設備和普通設備
上,他們表現(xiàn)出來了不同的大小。
單位坐標——對于與圖片大小或是圖層邊界相關(guān)的顯示,單位坐標是一個方便的
度量方式,當大小改變的時候,也不需要再次調(diào)整。單位坐標在OpenGL這種
紋理坐標系統(tǒng)中用得很多,CoreAnimation中也用到了單位坐標。
默認的contentsRect是{0,0,1,1},也就是說整個原圖默認可見,如果我們指定一個小一點的區(qū)域,那么layer上顯示的將會是contents對應的原圖中的一部分區(qū)域。比如設置為{0,0,0.5,0.5},如下圖

顯示的是右側(cè)的樣紙~~~
contentsRect最有意義的地方在于一種稱為image sprites(圖片拼合)的技術(shù)。這在游戲開發(fā)中應用特別廣泛。
該技術(shù)典型的用法是,將一系列圖片整合到一張大圖上一次性載入,相比多次載入多張小圖,這樣做極大的能減少載入時間以及少許的內(nèi)存使用和渲染性能。
現(xiàn)在網(wǎng)上有許多圖片整合工具,如TexturePacker等等。(友情提醒:該工具整合圖片時,有個trim屬性,設置是否將png圖片透明區(qū)域去除,最好關(guān)掉哈~保持原來的切圖。更具體的用法不清楚的可以留言哈~~)
假設我們通過工具已經(jīng)將多張圖片整合到一起了,那么相應的會生成一個數(shù)據(jù)文件,用來記錄圖片名、圖片在當前大圖中的位置等信息。如下圖

我們就可以根據(jù)該數(shù)據(jù)文件,從大圖中解析出我們要的每個圖片了。
經(jīng)過測試后,使用拼合圖,加載速度基本可以提升幾倍。
我自測時弄了1700多張小圖,整合成大圖后5M多。
4s讀取5M的拼合圖僅需0.002-0.006秒,而依次讀取1700多張小圖耗時4-5秒。讀取并將圖片渲染到界面上,拼合圖需0.1-0.2秒,而依次讀取小圖4-5秒以上?!卷槑б部梢园l(fā)現(xiàn),渲染和加載圖片的耗時占比,渲染貌似小得多呢~~】
當然,當圖片量不多就沒有必要啦~~~
所以,這技術(shù)也就在游戲開發(fā)中吃得開,畢竟游戲各種動效大部分都靠圖片~需要大量的圖片才玩得動~~
最后附上顯示的核心代碼。在這里我將加載大圖到內(nèi)存后,然后相應的把每個小圖顯示的scrollView上。
//加載拼合圖
- (CGFloat)loadSubLayers
{
CGFloat totalHeight = 0;
CGFloat maxWidth = 0;
NSDate *begin = [NSDate date];
UIImage *image = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:self.data[@"meta"][@"image"] ofType:nil]];
CGFloat imageWidth = CGImageGetWidth(image.CGImage);
CGFloat imageHeight = CGImageGetHeight(image.CGImage);
for (NSString *item in [self.data[@"frames"] allKeys]) {
@autoreleasepool {
#ifdef NEEDDRAW
NSDictionary *frameDict = self.data[@"frames"][item];
CGFloat width = [frameDict[@"w"] floatValue];
CGFloat height = [frameDict[@"h"] floatValue];
CGRect contentRect = CGRectMake([frameDict[@"x"] floatValue] / imageWidth, [frameDict[@"y"] floatValue] / imageHeight, width / imageWidth, height / imageHeight);
CALayer *layer = [CALayer layer];
layer.contents = (__bridge id _Nullable)(image.CGImage);
layer.contentsGravity = kCAGravityResizeAspect;
layer.contentsRect = contentRect;
layer.frame = CGRectMake(0, totalHeight, width, height);
layer.backgroundColor = [UIColor colorWithWhite:0.5 alpha:1].CGColor;
totalHeight += layer.bounds.size.height;
maxWidth = MAX(maxWidth, layer.frame.size.width);
[self.scrollView.layer addSublayer:layer];
#endif
}
}
NSDate *end = [NSDate date];
CGFloat timeStamp = [end timeIntervalSinceDate:begin];
self.scrollView.contentSize = CGSizeMake(maxWidth, totalHeight);
return timeStamp;
}
//加載小圖
- (CGFloat)loadSubLayers1
{
CGFloat height = 0;
CGFloat maxWidth = 0;
NSDate *begin = [NSDate date];
for (NSString *item in [self.data[@"frames"] allKeys]) {
@autoreleasepool {
NSString *imageName = item;
UIImage *image = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:imageName ofType:@"png"]];
#ifdef NEEDDRAW
CALayer *layer = [CALayer layer];
layer.contents = (__bridge id _Nullable)(image.CGImage);
layer.frame = CGRectMake(0, height, CGImageGetWidth(image.CGImage), CGImageGetHeight(image.CGImage));
layer.backgroundColor = [UIColor colorWithWhite:0.5 alpha:1].CGColor;
height += layer.bounds.size.height;
maxWidth = MAX(maxWidth, layer.frame.size.width);
[self.scrollView.layer addSublayer:layer];
#endif
}
}
NSDate *end = [NSDate date];
CGFloat timeStamp = [end timeIntervalSinceDate:begin];
self.scrollView.contentSize = CGSizeMake(maxWidth, height);
return timeStamp;
}