iOS圓角的離屏渲染,你真的弄明白了嗎

測試環境算法

Xcode 11.5app

iPhone 11 Pro Simulator測試

iOS 13.5flex

1. 如何設置圓角纔會觸發離屏渲染

咱們常常看到,圓角會觸發離屏渲染。但其實這個說法是不許確的,由於圓角觸發離屏渲染也是有條件的!優化

咱們先來看看蘋果官方文檔對於cornerRadius的描述:ui

Setting the radius to a value greater than 0.0 causes the layer to begin drawing rounded corners on its background. By default, the corner radius does not apply to the image in the layer’s contents property; it applies only to the background color and border of the layer. However, setting the masksToBounds property to true causes the content to be clipped to the rounded corners.spa

咱們發現設置cornerRadius大於0時,只爲layer的backgroundColorborder設置圓角;而不會對layer的contents設置圓角,除非同時設置了layer.masksToBoundstrue(對應UIView的clipsToBounds屬性)。3d

若是這時,你認爲layer.masksToBounds或者clipsToBounds設置爲true就會觸發離屏渲染,這是不徹底正確的。code

咱們先打開模擬器的離屏渲染顏色標記:orm

  • 不設置layer.masksToBounds或者clipsToBounds,其默認值爲NO
- (void)viewDidLoad {
    [super viewDidLoad];
    UIView *view1 = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 200.0, 200.0)];
    // 設置背景色
    view1.backgroundColor = UIColor.redColor;
    // 設置邊框寬度和顏色
    view1.layer.borderWidth = 2.0;
    view1.layer.borderColor = UIColor.blackColor.CGColor;
    // 設置圓角
    view1.layer.cornerRadius = 100.0;
    
    view1.center = self.view.center;
    [self.view addSubview:view1];
}
複製代碼

咱們看到只有背景色、邊框以及圓角的時候,確實不會觸發離屏渲染。

  • 設置layer.masksToBounds或者clipsToBoundsYES
- (void)viewDidLoad {
    [super viewDidLoad];
    UIView *view1 = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 200.0, 200.0)];
    // 設置背景色
    view1.backgroundColor = UIColor.redColor;
    // 設置邊框寬度和顏色
    view1.layer.borderWidth = 2.0;
    view1.layer.borderColor = UIColor.blackColor.CGColor;
    // 設置圓角
    view1.layer.cornerRadius = 100.0;
  
    // 設置裁剪
    view1.clipsToBounds = YES;
    
    view1.center = self.view.center;
    [self.view addSubview:view1];
}
複製代碼

當咱們開啓layer.masksToBounds或者clipsToBounds時,一樣的沒有觸發離屏渲染。這是由於咱們尚未設置圖片。

  • 設置layer.masksToBounds或者clipsToBoundsYES,同時設置圖片
- (void)viewDidLoad {
    [super viewDidLoad];
    UIView *view1 = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 200.0, 200.0)];
    // 設置背景色
    view1.backgroundColor = UIColor.redColor;
    // 設置邊框寬度和顏色
    view1.layer.borderWidth = 2.0;
    view1.layer.borderColor = UIColor.blackColor.CGColor;
    
    //設置圖片
    view1.layer.contents = (__bridge id)[UIImage imageNamed:@"pkq"].CGImage;
    
    // 設置圓角
    view1.layer.cornerRadius = 100.0;
    // 設置裁剪
    view1.clipsToBounds = YES;
    view1.center = self.view.center;
    [self.view addSubview:view1];
}
複製代碼

當咱們開啓layer.masksToBounds或者clipsToBounds時,同時設置圖片時,就會觸發離屏渲染。

  • 其實不光是圖片,咱們爲視圖添加一個有顏色、內容或邊框等有圖像信息的子視圖也會觸發離屏渲染。

    有圖像信息還包括在視圖或者layer的draw方法中進行繪製等。

- (void)viewDidLoad {
    [super viewDidLoad];
    UIView *view1 = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 200.0, 200.0)];
    // 設置背景色
    view1.backgroundColor = UIColor.redColor;
    // 設置邊框寬度和顏色
    view1.layer.borderWidth = 2.0;
    view1.layer.borderColor = UIColor.blackColor.CGColor;
    // 設置圓角
    view1.layer.cornerRadius = 100.0;
    // 設置裁剪
    view1.clipsToBounds = YES;
    
    // 子視圖
    UIView *view2 = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100.0, 100.0)];
    // 下面3個任何一個屬性
    // 設置背景色
    view2.backgroundColor = UIColor.blueColor;
    // 設置內容
    view2.layer.contents = (__bridge id)([UIImage imageNamed:@"pkq"].CGImage);
    // 設置邊框
    view2.layer.borderWidth = 2.0; 
    view2.layer.borderColor = UIColor.blackColor.CGColor;
    [view1 addSubview:view2];
    
    view1.center = self.view.center;
    [self.view addSubview:view1];
}
複製代碼

2. 圓角觸發離屏渲染的真正緣由

圖層的疊加繪製大概遵循「畫家算法」。

油畫算法:先繪製場景中的離觀察者較遠的物體,再繪製較近的物體。

先繪製紅色部分,再繪製⻩色部分,最後再繪製灰⾊部分,便可解決隱藏面消除的問題。即將場景按照物理距離和觀察者的距離遠近排序,由遠及近的繪製便可。

油畫算法

當咱們設置了cornerRadius以及masksToBounds進行圓角+裁剪時,masksToBounds裁剪屬性會應用到全部的圖層上。

原本咱們從後往前繪製,繪製完一個圖層就能夠丟棄了。但如今須要依次在 Offscreen Buffer中保存,等待圓角+裁剪處理,即引起了 離屏渲染

  • 背景色、邊框、背景色+邊框,再加上圓角+裁剪,根據文檔說明,由於 contents = nil 沒有須要裁剪處理的內容,因此masksToBounds設置爲YES或者NO都沒有影響。

  • 一旦咱們 爲contents設置了內容 ,不管是圖片、繪製內容、有圖像信息的子視圖等,再加上圓角+裁剪,就會觸發離屏渲染。

    不必定是直接爲contents賦值!

3. iOS9及之後的優化

關於圓角,iOS 9及以後的系統版本,蘋果進行了一些優化。

  • layer.contents/imageView.image

    咱們只設置contents或者UIImageViewimage,並加上圓角+裁剪,是不會產生離屏渲染的。但若是加上了背景色、邊框或其餘有圖像內容的圖層,仍是會產生離屏渲染。

    - (void)viewDidLoad {
        [super viewDidLoad];
        UIView *view1 = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 200.0, 200.0)];
        //設置圖片
        view1.layer.contents = (__bridge id)[UIImage imageNamed:@"qiyu"].CGImage;
        // 設置圓角
        view1.layer.cornerRadius = 100.0;
        // 設置裁剪
        view1.clipsToBounds = YES;
        
        view1.center = self.view.center;
        [self.view addSubview:view1];
    }
    複製代碼

其實這也是能夠理解的,由於只有 單層 內容須要添加圓角和裁切,因此能夠不須要用到離屏渲染技術。但若是加上了背景色、邊框或其餘有圖像內容的圖層,就會產生爲 多層 添加圓角和裁切,因此仍是會觸發離屏渲染(如1中的第3個例子)。

因此,咱們在使用相似於UIButton的視圖的時候須要注意:

  • UIButton

    - (void)viewDidLoad {
        [super viewDidLoad];
        self.view.backgroundColor = [UIColor whiteColor];
        // 建立一個button視圖
        UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 200.0, 200.0)];
        //設置圖片
        [button setImage:[UIImage imageNamed:@"pkq"] forState:UIControlStateNormal];
        button.center = self.view.center;
        [self.view addSubview:button];
    }
    複製代碼

    咱們爲UIButton設置一個圖片,其實會添加一個UIImageView

  • UIButton添加圓角和裁剪,則會觸發離屏渲染。

    // 設置圓角
    button.layer.cornerRadius = 100.0;
    // 設置裁剪
    button.clipsToBounds = YES;
    複製代碼
  • 若是改成UIButton中的UIImageView添加圓角和裁剪,則 不會觸發離屏渲染

    // 設置圓角
    button.imageView.layer.cornerRadius = 100.0;
    // 設置裁剪
    button.imageView.clipsToBounds = YES;
    複製代碼

擴展閱讀

更多渲染問題能夠看下面這篇文章。


若是以爲本文對你有所幫助,給我點個贊吧~

相關文章
相關標籤/搜索