Core Animation (一) contents

一、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;

}

參考自:iOS-Core-Animation-Advanced-Techniques

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容