2.1 contents屬性

2018-02-24 15:07 更新

contents屬性

CALayer 有一個(gè)屬性叫做contents,這個(gè)屬性的類型被定義為id,意味著它可以是任何類型的對(duì)象。在這種情況下,你可以給contents屬性賦任何值,你的app仍然能夠編譯通過(guò)。但是,在實(shí)踐中,如果你給contents賦的不是CGImage,那么你得到的圖層將是空白的。

contents這個(gè)奇怪的表現(xiàn)是由Mac OS的歷史原因造成的。它之所以被定義為id類型,是因?yàn)樵贛ac OS系統(tǒng)上,這個(gè)屬性對(duì)CGImage和NSImage類型的值都起作用。如果你試圖在iOS平臺(tái)上將UIImage的值賦給它,只能得到一個(gè)空白的圖層。一些初識(shí)Core Animation的iOS開(kāi)發(fā)者可能會(huì)對(duì)這個(gè)感到困惑。

頭疼的不僅僅是我們剛才提到的這個(gè)問(wèn)題。事實(shí)上,你真正要賦值的類型應(yīng)該是CGImageRef,它是一個(gè)指向CGImage結(jié)構(gòu)的指針。UIImage有一個(gè)CGImage屬性,它返回一個(gè)"CGImageRef",如果你想把這個(gè)值直接賦值給CALayer的contents,那你將會(huì)得到一個(gè)編譯錯(cuò)誤。因?yàn)镃GImageRef并不是一個(gè)真正的Cocoa對(duì)象,而是一個(gè)Core Foundation類型。

盡管Core Foundation類型跟Cocoa對(duì)象在運(yùn)行時(shí)貌似很像(被稱作toll-free bridging),他們并不是類型兼容的,不過(guò)你可以通過(guò)bridged關(guān)鍵字轉(zhuǎn)換。如果要給圖層的寄宿圖賦值,你可以按照以下這個(gè)方法:

layer.contents = (__bridge id)image.CGImage;

如果你沒(méi)有使用ARC(自動(dòng)引用計(jì)數(shù)),你就不需要__bridge這部分。但是,你干嘛不用ARC?!

讓我們來(lái)繼續(xù)修改我們?cè)诘谝徽滦陆ǖ墓こ?,以便能夠展示一張圖片而不僅僅是一個(gè)背景色。我們已經(jīng)用代碼的方式建立一個(gè)圖層,那我們就不需要額外的圖層了。那么我們就直接把layerView的宿主圖層的contents屬性設(shè)置成圖片。

清單2.1 更新后的代碼。

@implementation ViewController

- (void)viewDidLoad
{
  [super viewDidLoad]; //load an image
  UIImage *image = [UIImage imageNamed:@"Snowman.png"];

  //add it directly to our view's layer
  self.layerView.layer.contents = (__bridge id)image.CGImage;
}
@end

圖表2.1 在UIView的宿主圖層中顯示一張圖片

我們用這些簡(jiǎn)單的代碼做了一件很有趣的事情:我們利用CALayer在一個(gè)普通的UIView中顯示了一張圖片。這不是一個(gè)UIImageView,它不是我們通常用來(lái)展示圖片的方法。通過(guò)直接操作圖層,我們使用了一些新的函數(shù),使得UIView更加有趣了。

contentGravity

你可能已經(jīng)注意到了我們的雪人看起來(lái)有點(diǎn)。。。胖 ==! 我們加載的圖片并不剛好是一個(gè)方的,為了適應(yīng)這個(gè)視圖,它有一點(diǎn)點(diǎn)被拉伸了。在使用UIImageView的時(shí)候遇到過(guò)同樣的問(wèn)題,解決方法就是把contentMode屬性設(shè)置成更合適的值,像這樣:

view.contentMode = UIViewContentModeScaleAspectFit;

這個(gè)方法基本和我們遇到的情況的解決方法已經(jīng)接近了(你可以試一下 :) ),不過(guò)UIView大多數(shù)視覺(jué)相關(guān)的屬性比如contentMode,對(duì)這些屬性的操作其實(shí)是對(duì)對(duì)應(yīng)圖層的操作。

CALayer與contentMode對(duì)應(yīng)的屬性叫做contentsGravity,但是它是一個(gè)NSString類型,而不是像對(duì)應(yīng)的UIKit部分,那里面的值是枚舉。contentsGravity可選的常量值有以下一些:

  • kCAGravityCenter
  • kCAGravityTop
  • kCAGravityBottom
  • kCAGravityLeft
  • kCAGravityRight
  • kCAGravityTopLeft
  • kCAGravityTopRight
  • kCAGravityBottomLeft
  • kCAGravityBottomRight
  • kCAGravityResize
  • kCAGravityResizeAspect
  • kCAGravityResizeAspectFill

cotentMode一樣,contentsGravity的目的是為了決定內(nèi)容在圖層的邊界中怎么對(duì)齊,我們將使用kCAGravityResizeAspect,它的效果等同于UIViewContentModeScaleAspectFit, 同時(shí)它還能在圖層中等比例拉伸以適應(yīng)圖層的邊界。

self.layerView.layer.contentsGravity = kCAGravityResizeAspect;

圖2.2 可以看到結(jié)果

圖2.3 用錯(cuò)誤的contentsScale屬性顯示Retina圖片

如你所見(jiàn),我們的雪人不僅有點(diǎn)大還有點(diǎn)像素的顆粒感。那是因?yàn)楹蚒IImage不同,CGImage沒(méi)有拉伸的概念。當(dāng)我們使用UIImage類去讀取我們的雪人圖片的時(shí)候,他讀取了高質(zhì)量的Retina版本的圖片。但是當(dāng)我們用CGImage來(lái)設(shè)置我們的圖層的內(nèi)容時(shí),拉伸這個(gè)因素在轉(zhuǎn)換的時(shí)候就丟失了。不過(guò)我們可以通過(guò)手動(dòng)設(shè)置contentsScale來(lái)修復(fù)這個(gè)問(wèn)題(如2.2清單),圖2.4是結(jié)果

@implementation ViewController

- (void)viewDidLoad
{
  [super viewDidLoad]; //load an image
  UIImage *image = [UIImage imageNamed:@"Snowman.png"]; //add it directly to our view's layer
  self.layerView.layer.contents = (__bridge id)image.CGImage; //center the image
  self.layerView.layer.contentsGravity = kCAGravityCenter;

  //set the contentsScale to match image
  self.layerView.layer.contentsScale = image.scale;
}

@end

圖2.5 使用masksToBounds來(lái)修建圖層內(nèi)容

contentsRect

CALayer的contentsRect屬性允許我們?cè)趫D層邊框里顯示寄宿圖的一個(gè)子域。這涉及到圖片是如何顯示和拉伸的,所以要比contentsGravity靈活多了

bounds,frame不同,contentsRect不是按點(diǎn)來(lái)計(jì)算的,它使用了單位坐標(biāo),單位坐標(biāo)指定在0到1之間,是一個(gè)相對(duì)值(像素和點(diǎn)就是絕對(duì)值)。所以他們是相對(duì)與寄宿圖的尺寸的。iOS使用了以下的坐標(biāo)系統(tǒng):

  • 點(diǎn) —— 在iOS和Mac OS中最常見(jiàn)的坐標(biāo)體系。點(diǎn)就像是虛擬的像素,也被稱作邏輯像素。在標(biāo)準(zhǔn)設(shè)備上,一個(gè)點(diǎn)就是一個(gè)像素,但是在Retina設(shè)備上,一個(gè)點(diǎn)等于2*2個(gè)像素。iOS用點(diǎn)作為屏幕的坐標(biāo)測(cè)算體系就是為了在Retina設(shè)備和普通設(shè)備上能有一致的視覺(jué)效果。
  • 像素 —— 物理像素坐標(biāo)并不會(huì)用來(lái)屏幕布局,但是仍然與圖片有相對(duì)關(guān)系。UIImage是一個(gè)屏幕分辨率解決方案,所以指定點(diǎn)來(lái)度量大小。但是一些底層的圖片表示如CGImage就會(huì)使用像素,所以你要清楚在Retina設(shè)備和普通設(shè)備上,他們表現(xiàn)出來(lái)了不同的大小。
  • 單位 —— 對(duì)于與圖片大小或是圖層邊界相關(guān)的顯示,單位坐標(biāo)是一個(gè)方便的度量方式, 當(dāng)大小改變的時(shí)候,也不需要再次調(diào)整。單位坐標(biāo)在OpenGL這種紋理坐標(biāo)系統(tǒng)中用得很多,Core Animation中也用到了單位坐標(biāo)。

默認(rèn)的contentsRect是{0, 0, 1, 1},這意味著整個(gè)寄宿圖默認(rèn)都是可見(jiàn)的,如果我們指定一個(gè)小一點(diǎn)的矩形,圖片就會(huì)被裁剪(如圖2.6)

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)