玩轉iOS開發:4.《Core Animation》CALayer的視覺效果

CainLuo發表於2016-10-28

文章分享至我的個人部落格: https://cainluo.github.io/14775511877452.html


作者感言

前一章我們對CALayer瞭解的更加深入了一些《Core Animation》CALayer的幾何圖層今天我們就來講講CALayer Visual Effects, 也就是CALayer的視覺效果.

**最後:** **如果你有更好的建議或者對這篇文章有不滿的地方, 請聯絡我, 我會參考你們的意見再進行修改, 聯絡我時, 請備註**`Core Animation`**如果覺得好的話, 希望大家也可以打賞一下~嘻嘻~祝大家學習愉快~謝謝~**

簡介

CALayer Visual Effects講得是CALayer一些我們能夠看得見的東西, 這些知識點在我們日常開發中也會有用到的, 比如Rounded Corners, Layer Borders, Drop Shadows, Layer Masking, Scaling Filters, Group Opacity等等, 待我們一一去講解.


Rounded Corners

Rounded Corners這個東西我們用的其實也是挺多的, 我們都知道, 在iOS 7之前, 基本上所有的Button都是橢圓形的, 而這些Button之所以都是橢圓形, 大多數都是因為Rounded Corners這個東西的原因. 在CALayer有一個叫conrnerRadius的CGFloat型別屬性來控制著圖層角的曲率, 預設值為0, 你可以設定任意數值, 是的圖層角顯示不一樣的曲率, 而且conrnerRadius這個屬性所影響的到的只有設定該屬性的CALayer, 並不會影響到子圖層或者是背景圖, 但如果你要讓子圖層或者是背景圖也要跟著該CALayer進行曲率處理, 你可以把masksToBounds設定為YES, 這樣子就可以滿足你的需求了. 我們直接來看看Demo吧:

- (void)layerRoundedCorners {
    
    UIView *view = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
    
    view.backgroundColor = [UIColor redColor];
    view.layer.cornerRadius = 50.f;
    view.layer.masksToBounds = YES;
    
    CALayer *layer = [CALayer layer];
    
    layer.backgroundColor = [UIColor blueColor].CGColor;
    layer.frame = CGRectMake(50, 50, 50, 50);
    
    [view.layer addSublayer:layer];
    [self.view addSubview:view];
}
複製程式碼

1

2

PS: 這裡需要注意一點, 由於masksToBoundsconrnerRadius這兩個屬性同時使用是挺消耗效能的, 如果你是要大面積的去使用, 會造成卡頓現象, 比如說在UICollectionView或者是UITableView上使用.


Layer Borders

CALayer還有兩個更好玩的屬性, 這也是在iOS 7之前Button會預設實現的兩個屬性, 一個叫做borderWidth, 一個叫做borderColor.

  • borderWidth: 這是一個CGFloat型別的屬性, 是用來設定CALayer邊框寬度, 預設值是為0.
  • borderColor: 這是一個CGColorRef型別的屬性, 所以你不能給它直接設定一個UIColor物件, 前面我們已經對CGColorRef這個東東進行了簡單的介紹, 這裡我們只需要知道直接給它賦個值就好了, 還有就是borderColor是用來設定CALayer邊框顏色, 在iOS 7之前預設值是為黑色, iOS 7之後預設值是為透明.
我們直接來看Demo吧:
- (void)layerBorders {
    
    UIView *view = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
    
    view.backgroundColor = [UIColor blueColor];
    view.layer.borderWidth = 5.f;
    view.layer.borderColor = [UIColor redColor].CGColor;
    
    [self.view addSubview:view];
}
複製程式碼

3

4


Drop Shadows

iOS當中, 還有一個特性, 叫做Drop Shadows(陰影), 其實這個也不算是iOS特有的, 畢竟在Mac OS裡早就已經有這個特性了. 陰影往往是起暗示作用, 比如說你現在正在顯示的視窗, 周邊就會帶上陰影, 或者是強調裡面的某個圖層的優先順序, 大多數時候只是用來裝飾罷了. 設定陰影的時候也是很簡單, 直接給shadowOpacity屬性設定一個大於0.0的值就好了, shadowOpacity這個屬性可設定的值是在0.0~1.0之間, 預設是0.f, 最大值是1.f, 如果直接設定為1.f, 那麼將會顯示一個輕微模糊的黑色陰影在圖層的上方, 另外, 你可以通過CALayer所提供的shadowColor, shadowOffset, shadowRadius對陰影進行一些額外的操作, 這裡就不一一介紹了, 各位童鞋們可以自行嘗試一下~ 說到這裡, 會有人問, 為什麼陰影是在圖層的上方呢? 其實在之前我們就有了解過Mac OS和iOS的一些東西, 基本上iOS的一些東西都是從Mac OS搬過來, 然後再改改的, 包括這個陰影也是如此, 所以你才會看到在iOS當中陰影是反過來的, 這裡我們只需要設定一下shadowOffset就可以正常顯示了. 直接看Demo吧:

- (void)layerDropShadows {
    
    UIView *view = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
    
    view.backgroundColor = [UIColor redColor];
    view.layer.shadowOpacity = 1.0f;
    view.layer.shadowOffset = CGSizeMake(0, 3);
    view.layer.shadowRadius = 10.f;
    view.layer.shadowColor = [UIColor blueColor].CGColor;
    
    [self.view addSubview:view];
}
複製程式碼

5

6

Shadow Clipping

這裡還有一個比較好玩的東西, 就是陰影剪下, 我們都知道CALayer超出了UIView的範圍, 如果要剪下掉的話, 只能用masksToBounds這個屬性去剪下. 但這樣子會帶來另一個的問題, 因為一旦使用masksToBounds這個屬性, 剪下掉的就不只是多出來的部分, 會連陰影部分都一起剪下完, 為了防止陰影也跟著被剪下掉, 我們需要使用一個比較笨的方法, 就是再建立多一個CALayer, 讓它去建立陰影部分, 另一個CALayer去剪下內容就可以了. 我們還是直接看Demo吧:

- (void)layerShadowClipping {
    
    UIView *contentView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
    UIView *blueView = [[UIView alloc] initWithFrame:CGRectMake(50, 50, 100, 100)];
    UIView *shadowView = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
    
    contentView.backgroundColor = [UIColor grayColor];
    blueView.backgroundColor = [UIColor blueColor];
    
    contentView.layer.masksToBounds = YES;
    
    shadowView.layer.shadowOpacity = 1.0f;
    shadowView.layer.shadowOffset = CGSizeMake(0, 0.5f);
    shadowView.layer.shadowColor = [UIColor redColor].CGColor;

    [contentView addSubview:blueView];
    [shadowView addSubview:contentView];
    
    [self.view addSubview:shadowView];
}
複製程式碼

7

8

shadowPath

我們都知道其實陰影是沒有形態的, 它是根據圖層的形狀來進行顯示, 如果在一個檢視當中有很多子圖層, 然後要一個一個的去計算陰影的形狀, 那是非常耗效能的, 但如果在開發之前你就已經知道陰影的形狀, 那麼你就可以提前設定好, 這樣子就可以優化效能了, 而這個屬性就是shadowPath, shadowPathCGPathRef型別, 可以說是一個指向CGPath的指標, 而CGPath是一個Core Graphics物件, 可以用來任意描繪一個向量圖形, 我們也可以使用它來描繪陰影, 大大的提升效能. 我們直接來看Demo吧:

- (void)layerShadowPath {
    
    UIView *shadowViewOne = [[UIView alloc] initWithFrame:CGRectMake(50, 50, 100, 100)];
    UIView *shadowViewTwo = [[UIView alloc] initWithFrame:CGRectMake(50, 250, 100, 100)];

    shadowViewOne.layer.shadowOpacity = 0.5f;
    shadowViewTwo.layer.shadowOpacity = 0.5f;
    
    CGMutablePathRef squarePath = CGPathCreateMutable();
    CGPathAddRect(squarePath, NULL, shadowViewOne.bounds);
    shadowViewOne.layer.shadowPath = squarePath;
    
    CGPathRelease(squarePath);
    
    CGMutablePathRef circlePath = CGPathCreateMutable();
    CGPathAddEllipseInRect(circlePath, NULL, shadowViewTwo.bounds);
    shadowViewTwo.layer.shadowPath = circlePath;
    
    CGPathRelease(circlePath);

    [self.view addSubview:shadowViewOne];
    [self.view addSubview:shadowViewTwo];
}
複製程式碼

9

10


Layer Masking

我們都知道如果我們要剪下超出範圍的圖層可以使用masksToBounds, 通過conrnerRadius可以設定圖層為圓角, 但如果你需要設定一個不規整的圖層時, 用上面的兩個屬性是沒法實現的. 雖然我們可以使用一個32位且帶有alpha通道的png圖片可以實現, 但這個方法也是有侷限的, 不能以動態編碼的形式生成蒙版, 也不能讓子圖層或者子檢視也裁剪成同樣的形狀. 蘋果為了解決這個為題, 在CALayer當中提供了一個CGLayer型別, 名叫mask的屬性, 有著和其他CALayer一樣的佈局屬性, 它就像是一個子圖層, 相對於父圖層來進行佈局的, 但它又區別於子圖層, 它是定義父圖層可見部分的. 雖然mask裡也有Color這個屬性, 但它並沒啥用, 真正重要的是圖層的輪廓, 它就像是一臺切割機一樣, 實心圖層部分會被保留, 其他的就會作為垃圾一樣被拋棄. 我們直接來看Demo吧:

- (void)layerMasking {
    
    UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
    
    imageView.image = [UIImage imageNamed:@"arrow"];
    
    CALayer *maskLayer = [CALayer layer];
    UIImage *maskImage = [UIImage imageNamed:@"star"];
    
    maskLayer.frame = imageView.bounds;
    maskLayer.contents = (__bridge id _Nullable)(maskImage.CGImage);
    
    imageView.layer.mask = maskLayer;
    
    [self.view addSubview:imageView];
}
複製程式碼

11

12


Scaling Filters

這裡我們提及一個點, 是關於圖片顯示的一個問題, 我們都知道如果要讓一個CALayer顯示一張圖片是直接給它的contents直接設定內容, 但是呢, 這個圖片是否正確的顯示, 畫質如何, 我們都無從而知. 為了解決這個問題,CALayer分別提供了minificationFiltermagnificationFilter屬性, 它們都是NSString型別, 說到這裡, 有人會疑問, 直接顯示不就是最好的麼, 為啥要那麼麻煩, 其實不一定的, 原因的話, 大概有三點吧:

  • 能夠顯示最好的畫質, 指的是畫素沒有被壓縮也沒有被拉伸.
  • 可以節省資源, 比如神馬記憶體, 儲存之類的.
  • 可以優化效能, 減輕CPU的壓力.

打個比方, 比如我們開發的時候, 有個頭像省略圖, 這個時候呢, 你說是用全圖比較好, 還是壓縮過的比較好? 答案肯定是壓縮過的比較好, 因為可以省資源. 順便說說, 這兩個屬性可設定的值分別是:

  • kCAFilterLinear(預設值)
  • kCAFilterNearest
  • kCAFilterTrilinear 說那麼多, 直接來看Demo吧:
- (void)layerScalingFilters {
    
    UIImageView *imageViewOne = [[UIImageView alloc] initWithFrame:CGRectMake(0, 100, self.view.bounds.size.width / 2, 200)];
    UIImageView *imageViewTwo = [[UIImageView alloc] initWithFrame:CGRectMake(self.view.bounds.size.width / 2, 100, self.view.bounds.size.width / 2, 200)];
    UIImageView *imageViewThree = [[UIImageView alloc] initWithFrame:CGRectMake(0, 300, self.view.bounds.size.width / 2, 200)];
    
    imageViewOne.image = [UIImage imageNamed:@"expression"];
    imageViewTwo.image = [UIImage imageNamed:@"expression"];
    imageViewThree.image = [UIImage imageNamed:@"expression"];

    imageViewOne.layer.magnificationFilter = kCAFilterNearest;
    imageViewTwo.layer.magnificationFilter = kCAFilterLinear;
    imageViewThree.layer.magnificationFilter = kCAFilterTrilinear;

    imageViewOne.layer.minificationFilter = kCAFilterNearest;
    imageViewTwo.layer.minificationFilter = kCAFilterLinear;
    imageViewThree.layer.minificationFilter = kCAFilterTrilinear;
    
    [self.view addSubview:imageViewOne];
    [self.view addSubview:imageViewTwo];
    [self.view addSubview:imageViewThree];
}
複製程式碼

13

14

額...這個有些尷尬, 看起來的效果區別不太大...湊合著看吧..


Group Opacity

最後, 我們來說說Group Opacity這個東西, 在iOS 7之前, 如果你在一個Button上新增一個檢視, 在alpha設定為50%的情況下會出現圖層有分割, 需要設定shouldRasterizerasterizationScale兩個屬性才能使得在半透明的情況下看起來是一體, 但是這個問題在iOS 7之後就已經解決了, 現在shouldRasterize屬性更多的是用來解決UITableView裡的CALayer圓角顯示圖層的處理, 這裡就不做多的解釋了, 有興趣的童鞋們可以去谷歌搜搜shouldRasterize的用法, 都是挺簡單的~


總結

總結一下, 這一章我們瞭解更多的是CALayer肉眼上能看到的東西, 比如:

  • Rounded Corners: 在這個知識點裡, 我們瞭解了CALayer圓角的處理.
  • Layer Borders: 在這個知識點裡, 我們瞭解了CALayer邊框的處理, 包括邊框的厚度, 以及顏色等
  • Drop Shadows: 在這個知識點裡, 我們知道了CALayer陰影的處理.
  • Layer Masking: 在這個知識點裡, 我們瞭解了CALayer可以作為一個mask去使用, 並且可以顯示不同不規則的形狀.
  • Scaling Filters: 在這個知識點裡, 我們知道CALayer可以更改顯示的畫質, 優化顯示的資源等等.
  • Group Opacity: 在這個知識點裡, 雖然在iOS 7之後已經解決了這個問題, 但我們也可以把shouldRasterize屬性運用在其他的地方.

工程地址

專案地址: https://github.com/CainRun/CoreAnimation


最後

碼字很費腦, 看官賞點飯錢可好

微信

支付寶

相關文章