Core Animation 第二章 寄宿圖

序章
第一章

Paste_Image.png

contents屬性


contentsCALayer的一個(gè)屬性,類型為id,但如果你用CGImage以外的對象對其賦值的話你只能得到一個(gè)空的圖層。

contents之所以是id類型書中的解釋為:在macOS中,CGImageNSImage對象都可以對contents屬性起作用。

還需要注意的就是UIImageCGImage 屬性實(shí)際是一個(gè) CGImageRef的結(jié)構(gòu)體,所以你需要使用__bridge來進(jìn)行橋接。

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

書中提到如果是MRC環(huán)境下,則不需要__bridge,不過現(xiàn)在MRC已經(jīng)成為歷史了。

接下來你可以新建一個(gè)工程或者繼續(xù)使用上一章使用的工程,在根控制器的View中添加一個(gè)空白的UIView,作為我們要使用的layerView,設(shè)置contents屬性。

- (void)viewDidLoad {
    [super viewDidLoad];
    UIImage *image = [UIImage imageNamed:@"pica"];
    self.layerView.layer.contents = (__bridge id)image.CGImage;
}
向UIView的主圖層中添加contents.png

這樣我們就可以不使用UIImageView來顯示圖片了。順便一提,如果你使用了UIImageView來顯示圖片的話,你會發(fā)現(xiàn)UIImageView.layercontents與你賦值的UIImageCGImage其實(shí)是同一個(gè)對象。

對比UIImageView.png

contentsGravity


再說這個(gè)屬性之前,我們先來對我們的皮卡丘做一點(diǎn)小改動,把200x200的layerView變成320x180,再來看一下,會發(fā)現(xiàn)我們的皮卡丘被拉伸了。

被拉伸的皮卡丘.png

熟悉UIKit的我們很快就能想到修改contentMode來處理圖片的拉伸狀態(tài)。

view.contentMode = UIViewContentModeScaleAspectFit;

而在CALayer中,這個(gè)屬性叫做contentsGravityNSString類型的變量,而contentMode則是一個(gè)則是一個(gè)對象,這里也可以看出UIViewCALayer進(jìn)行了封裝。contentsGravity可用的NSString常量如下:

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

與contentMode一樣,contentsGravity也是處理內(nèi)容在圖層邊界的對齊方式,下面我們使用 kCAGravityResizeAspect來處理我們的layerView。

self.layerView.layer.contentsGravity = kCAGravityResizeAspect;
處理contentsGravity后的皮卡丘.png

contentsScale


contentsScale決定了寄宿圖的像素尺寸和視圖大小的比例,默認(rèn)值為1.0。關(guān)于試圖大小,如果你接著使用上面的例子來設(shè)置contentsScale的話,你會發(fā)現(xiàn)contentsScale并沒有生效,因?yàn)槲覀円呀?jīng)設(shè)置了layer的邊界狀態(tài)。而且如果你只是想要放大圖片的話,我們后面還會說到一個(gè)更方便的屬性transform。
更多的時(shí)候我們真正使用到contentsScale屬性是為了適應(yīng)Retain屏幕。它用來判斷繪制圖層時(shí)應(yīng)該為寄宿圖創(chuàng)建的空間大小和需要顯示的拉伸程度(如果沒有設(shè)置contentsGravity的話),與UIViewcontentScaleFactor屬性類似。如果contentsScale為1.0則每個(gè)點(diǎn)分配一個(gè)像素,為2.0則每個(gè)點(diǎn)分配兩個(gè)像素用來繪制圖片。由于contentsGravity默認(rèn)值為resize,所以我們需要調(diào)整一下contentsGravity以方便我們看到實(shí)際效果。

- (void)viewDidLoad {
    [super viewDidLoad];
    UIImage *image = [UIImage imageNamed:@"pica"];
    self.layerView.layer.contents = (__bridge id)image.CGImage;
    self.layerView.layer.contentsGravity = kCAGravityCenter;
    self.layerView.layer.contentsScale = image.scale;
}
修改過contentsScale的皮卡丘.png

可以看到我們的皮卡丘不但被放大了,而且出現(xiàn)了一些像素化的情況,所以通常使用的時(shí)候應(yīng)該與屏幕的scale對應(yīng)一致。

self.layerView.layer.contentsScale = [UIScreen mainScreen].scale;

maskToBounds


這個(gè)屬性與UIView 的 clipsToBounds 類似,用來決定是否顯示超出邊界的內(nèi)容,設(shè)置為YES則不會顯示超出部分的內(nèi)容。我們可以使用剛剛的例子來測試一下。

- (void)viewDidLoad {
    [super viewDidLoad];
    UIImage *image = [UIImage imageNamed:@"pica"];
    self.layerView.layer.contents = (__bridge id)image.CGImage;
    self.layerView.layer.contentsGravity = kCAGravityCenter;
//    self.layerView.layer.contentsScale = [UIScreen mainScreen].scale;
    self.layerView.layer.contentsScale = image.scale;
    self.layerView.layer.masksToBounds = YES;
}
maskToBounds設(shè)置為YES的皮卡丘.png

contentsRect

contentsRect屬性允許我們只顯示寄宿圖的某一個(gè)區(qū)域。和bounds,frame不同的是contentsRect的單位不是點(diǎn),而是 0 到 1的一個(gè)單位。默認(rèn)的contentsRect是{0,0,1,1},也就是說整張寄宿圖都是可見的,如果我們制定一個(gè)小一點(diǎn)的矩形,那么圖片就會被剪裁,這里我直接使用書中的圖片來解釋。

一個(gè)自定義的 contentsRect (左)和之前顯示的內(nèi)容(右).png

下面我們來做一個(gè)簡單的例子,這是我們的VC:

StoryBoard中的控制器.png

這是我們要使用的圖片:

dribbble.png
@interface ContentsRectController ()
@property (strong, nonatomic) IBOutletCollection(UIView) NSArray *contentViews;
@end

@implementation ContentsRectController

- (void)viewDidLoad {
    [super viewDidLoad];
    UIImage *image = [UIImage imageNamed:@"dribbble-1"];
    CGFloat spaceX = 1.0 / 3.0;
    CGFloat spaceY = 1.0 / 2.0;
    for (int i = 0; i < 3; i ++) {
        for (int j = 0; j < 2; j ++) {
            //獲取collection中的view
            UIView *layerView = self.contentViews[i * 2 + j];
            layerView.layer.contents = (__bridge id)image.CGImage;
            //計(jì)算,設(shè)置contentsRect
            layerView.layer.contentsRect = CGRectMake(i * spaceX, j * spaceY, spaceX, spaceY);   
        }
    }
}

效果如下:

contentsRect效果展示.png

contentsCenter


首先,contentsCenter與位置無關(guān),他是一個(gè)CGRect類型的屬性,定一個(gè)一個(gè)固定的邊框和在圖層上可以拉伸的范圍。效果與UIImage的拉伸方法類似。例如我們將contentsCenter設(shè)置為{0.25, 0.25, 0.5, 0.5}則圖片的拉伸狀態(tài)如下:

contentsCenter.png

Custom Drawing


contents賦值CGImage并不是設(shè)置寄宿圖的唯一途徑,我們也可以使用Core Graphics直接繪制寄宿圖。我們可以通過繼承UIView并實(shí)現(xiàn)-drawRect:方法來自定義繪制。當(dāng)UIView檢測到-drawRect:方法被調(diào)用了,就會為視圖分配一個(gè)寄宿圖,這個(gè)寄宿圖的像素尺寸為視圖大小乘以contentsScale。也就是說如果你不需要寄宿圖,那么你最好不要實(shí)現(xiàn)這個(gè)方法,那樣的話會造成CPU資源和內(nèi)存的浪費(fèi)。

當(dāng)視圖出現(xiàn)在屏幕上的時(shí)候-drawRect:方法就會被調(diào)用,并且繪制的結(jié)果會被緩存起來,當(dāng)開發(fā)者調(diào)用-setNeedsDisplay或者影響視圖表現(xiàn)的屬性,如bounds時(shí),寄宿圖就會被更新。

-drawRect:是一個(gè)UIView的方法,實(shí)際是layer完成了繪制與緩存。當(dāng)寄宿圖需要被重繪的時(shí)候CALayer就會請求它的代理給它一個(gè)寄宿圖來顯示。

- (void)displayLayer:(CALayer *)layer;

如果這個(gè)方法沒有被實(shí)現(xiàn),CALayer就會嘗試下面的方法:

- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx;

接下來我們來實(shí)際繪制一個(gè)寄宿圖吧。

- (void)viewDidLoad {
    [super viewDidLoad];   
    CALayer *blueLayer = [CALayer layer];
    blueLayer.frame = CGRectMake(50.f, 50.f, 100.f, 100.f);
    blueLayer.backgroundColor = [UIColor blueColor].CGColor;
    blueLayer.delegate = self;
    blueLayer.contentsScale = [UIScreen mainScreen].scale;
    [self.layerView.layer addSublayer:blueLayer];
    [blueLayer display];
}

- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx {
    CGContextSetLineWidth(ctx, 10.0f);
    CGContextSetStrokeColorWithColor(ctx, [UIColor redColor].CGColor);
    CGContextStrokeEllipseInRect(ctx, layer.bounds);
}
實(shí)現(xiàn)CALayerDelegate來繪制圖層.png

需要注意的是:

  • 我們對blueLayer調(diào)用了display方法,這是因?yàn)镃ALayer不會因?yàn)楸伙@示到屏幕上就自動重繪。
  • 我們并沒有設(shè)置maskToBounds屬性,但是視圖依然被剪裁了,這是因?yàn)槭褂肅ALayerDelegate繪制寄宿圖的時(shí)候,并沒有對邊界外繪制提供支持。

總結(jié)


本章主要講述了 contents,寄宿圖相關(guān)的屬性,以及如何使用CALayerDelegate繪制寄宿圖。

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

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

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