頁面卡頓的優化--圓角

今天產品經理告訴我:誒,那個誰,這個界面很卡誒!!!你看看什麼狀況。。。因而我掏出了Instrument裏的Core Animation看看FPS,發現滑動的時候FPS特別低Orz!html

產生卡頓的緣由

首先,查閱資料看下爲何會產生卡頓的緣由。 ios

渲染機制.jpg
在 iOS 系統中,圖像內容展現到屏幕的過程須要 CPU 和 GPU 共同參與。CPU 負責計算顯示內容,好比視圖的建立、佈局計算、圖片解碼、文本繪製等。隨後 CPU 會將計算好的內容提交到 GPU 去,由 GPU 進行變換、合成、渲染。以後 GPU 會把渲染結果提交到幀緩衝區去,等待下一次 VSync 信號到來時顯示到屏幕上。因爲垂直同步的機制,若是在一個 VSync 時間內,CPU 或者 GPU 沒有完成內容提交,則那一幀就會被丟棄,等待下一次機會再顯示,而這時顯示屏會保留以前的內容不變。這就是界面卡頓的緣由。 那GPU何時會消耗呢,相對於CPU,GPU作的事情比較單一,接收提交的紋理(Texture)和頂點描述(三角形),應用變換(transform)、混合並渲染,而後輸出到屏幕上。寬泛的說,大多數 CALayer 的屬性都是用 GPU 來繪製。如下操做會下降GPU的性能:

大量幾何結構

全部的 Bitmap,包括圖片、文本、柵格化的內容,最終都要由內存提交到顯存,綁定爲 GPU Texture。不管是提交到顯存的過程,仍是 GPU 調整和渲染 Texture 的過程,都要消耗很多 GPU 資源。當在較短期顯示大量圖片時(好比 TableView 存在很是多的圖片而且快速滑動時),CPU 佔用率很低,GPU 佔用很是高,界面仍然會掉幀。避免這種狀況的方法只能是儘可能減小在短期內大量圖片的顯示,儘量將多張圖片合成爲一張進行顯示。 另外當圖片過大,超過 GPU 的最大紋理尺寸時,圖片須要先由 CPU 進行預處理,這對 CPU 和 GPU 都會帶來額外的資源消耗。算法

視圖的混合

當多個視圖(或者說 CALayer)重疊在一塊兒顯示時,GPU 會首先把他們混合到一塊兒。若是視圖結構過於複雜,混合的過程也會消耗不少 GPU 資源。爲了減輕這種狀況的 GPU 消耗,應用應當儘可能減小視圖數量和層次,而且減小沒必要要的透明視圖。佈局

離屏渲染

離屏渲染是指圖層在被顯示以前是在當前屏幕緩衝區之外開闢的一個緩衝區進行渲染操做。 離屏渲染須要屢次切換上下文環境:先是從當前屏幕(On-Screen)切換到離屏(Off-Screen);等到離屏渲染結束之後,將離屏緩衝區的渲染結果顯示到屏幕上又須要將上下文環境從離屏切換到當前屏幕,而上下文環境的切換是一項高開銷的動做。 會形成 offscreen rendering 的緣由有: 陰影(UIView.layer.shadowOffset/shadowRadius/…) 圓角(當 UIView.layer.cornerRadius 和 UIView.layer.maskToBounds 一塊兒使用時) 圖層蒙板 開啓光柵化(shouldRasterize = true)性能

圓角優化

因此咱們找到了緣由,因爲咱們的項目是基於地圖的,在地圖上直接添加頁面上去,以前地圖就有不少圓角設置,加上cell上的圓角,會形成GPU性能的損耗,因此相對來講頁面的滑動會損耗GPU啦。 因而回到了老生常談的問題了,關於圓角的優化。 以前看文章有說這樣去優化:優化

+ (void)cutRadiousWithView:(UIView *)view radious:(CGFloat)radious {
    UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:view.bounds byRoundingCorners:UIRectCornerAllCorners cornerRadii:CGSizeMake(radious, radious)];
    CAShapeLayer *maskLayer = [[CAShapeLayer alloc]init];
    //設置大小
    maskLayer.frame = view.bounds;
    //設置圖形樣子
    maskLayer.path = maskPath.CGPath;
    view.layer.mask = maskLayer;
}
複製代碼

然而發現並無什麼用,其實mask遮罩仍是會發生離屏渲染的。並且親測這樣的FPS貌似更低???(黑人問號臉)spa

圖片圓角優化

因而我繼續查資料,發現圖片的圓角能夠將圖片進行進行重繪,獲得一張新的圖片。方法以下:3d

+ (UIImage *)cutCircleImageWithImage:(UIImage *)image size:(CGSize)size radious:(CGFloat)radious {
    UIGraphicsBeginImageContextWithOptions(size, NO, [UIScreen mainScreen].scale);

    CGRect rect = CGRectMake(0, 0, size.width, size.height);
    CGContextAddPath(UIGraphicsGetCurrentContext(),
                 [UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:radious].CGPath);
    CGContextClip(UIGraphicsGetCurrentContext());

    [image drawInRect:rect];
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return newImage;
}
複製代碼

用這個方法能夠獲得新的圖片,對於圖片的圓角處理能夠這樣作,可是對於View的呢?code

View圓角優化

View的話能夠給他蓋一層ImageView,設置下Imageview的圖片,方法以下:orm

+ (void)cutCicleViewWithView:(UIView *)view radious:(CGFloat)radius {
    CGSize size = view.frame.size;
    CGRect rect = CGRectMake(0, 0, size.width, size.height);
    UIColor *bkColor = view.backgroundColor;

    UIImage *image = [[UIImage alloc] init];
    UIGraphicsBeginImageContextWithOptions(size, NO, UIScreen.mainScreen.scale);
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetFillColorWithColor(context, bkColor.CGColor);
    CGContextAddPath(context,
                 [UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:radius].CGPath);
    CGContextDrawPath(context, kCGPathFill);
    [image drawInRect:rect];
    UIImage *output = UIGraphicsGetImageFromCurrentImageContext();

    UIGraphicsEndImageContext();

    UIImageView *imageView = [[UIImageView alloc] initWithFrame:rect];
    imageView.image = output;
    [view insertSubview:imageView atIndex:0];
    view.backgroundColor = [UIColor clearColor];
}
複製代碼

總結

相信學過算法的同窗都知道,越快的算法,可能空間複雜度越高。當你爲了某一性能去優化,必然會損耗其餘的性能,所謂的算法優化是爲了達到最佳實現效果。因此這樣優化GPU的結果就是會損耗CPU。 過早的優化是魔鬼,可是到問題出來的時候 ,有些東西就有必要去作了!

參考資料:

http://www.reviewcode.cn/article.html?reviewId=7 http://ios.jobbole.com/92237/

相關文章
相關標籤/搜索