一般我們?cè)趇OS開發(fā)的過程中設(shè)置圓角都是如下這樣設(shè)置的。
avatarImageView.clipsToBounds = YES;
[avatarImageView.layer setCornerRadius:50];
這樣設(shè)置會(huì)觸發(fā)離屏渲染,比較消耗性能。比如當(dāng)一個(gè)頁面上有十幾頭像這樣設(shè)置了圓角會(huì)明顯感覺到卡頓。
注意:png圖片UIImageView處理圓角是不會(huì)產(chǎn)生離屏渲染的。(ios9.0之后不會(huì)離屏渲染,ios9.0之前還是會(huì)離屏渲染)。
設(shè)置圓角的方法
直接使用setCornerRadius
這種就是最常用的,也是最耗性能的。
setCornerRadius設(shè)置圓角之后,shouldRasterize=YES光柵化
avatarImageView.clipsToBounds = YES;
[avatarImageView.layer setCornerRadius:50];
avatarImageView.layer.shouldRasterize = YES;
avatarImageViewUrl.layer.rasterizationScale=[UIScreen mainScreen].scale; //UIImageView不加這句會(huì)產(chǎn)生一點(diǎn)模糊
shouldRasterize=YES設(shè)置光柵化,可以使離屏渲染的結(jié)果緩存到內(nèi)存中存為位圖,
使用的時(shí)候直接使用緩存,節(jié)省了一直離屏渲染損耗的性能。
但是如果layer及sublayers常常改變的話,它就會(huì)一直不停的渲染及刪除緩存重新
創(chuàng)建緩存,所以這種情況下建議不要使用光柵化,這樣也是比較損耗性能的。
直接覆蓋一張中間為圓形透明的圖片(推薦使用)
這種方法就是多加了一張透明的圖片,GPU計(jì)算多層的混合渲染 blending也是會(huì)消耗
一點(diǎn)性能的,但比第一種方法還是好上很多的。
UIImage drawInRect繪制圓角
這種方式GPU損耗低內(nèi)存占用大,而且UIButton上不知道怎么繪制,可以用
UIimageView添加個(gè)點(diǎn)擊手勢(shì)當(dāng)做UIButton使用。
UIGraphicsBeginImageContextWithOptions(avatarImageView.bounds.size, NO, [UIScreen mainScreen].scale);
[[UIBezierPath bezierPathWithRoundedRect:avatarImageView.bounds
cornerRadius:50] addClip];
[image drawInRect:avatarImageView.bounds];
avatarImageView.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
這段方法可以寫在SDWebImage的completed回調(diào)里,在主線程異步繪制。
也可以封裝到UIImageView里,寫了個(gè)DSRoundImageView。后臺(tái)線程異步繪制,不會(huì)阻塞主線程。
問題:這種方法圖片很多的話CUP消耗會(huì)高,內(nèi)存占用也會(huì)暴增,而且后臺(tái)線程繪制會(huì)比在主線程繪制占用更多的內(nèi)存,不知道怎么解決?求大神指教!
SDWebImage處理圖片時(shí)Core Graphics繪制圓角
//UIImage繪制為圓角
int w = imageSize.width;
int h = imageSize.height;
int radius = imageSize.width/2;
UIImage *img = image;
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = CGBitmapContextCreate(NULL, w, h, 8, 4 * w, colorSpace, kCGImageAlphaPremultipliedFirst);
CGRect rect = CGRectMake(0, 0, w, h);
CGContextBeginPath(context);
addRoundedRectToPath(context, rect, radius, radius);
CGContextClosePath(context);
CGContextClip(context);
CGContextDrawImage(context, CGRectMake(0, 0, w, h), img.CGImage);
CGImageRef imageMasked = CGBitmapContextCreateImage(context);
img = [UIImage imageWithCGImage:imageMasked];
CGContextRelease(context);
CGColorSpaceRelease(colorSpace);
CGImageRelease(imageMasked);
以上代碼我寫成了UIImage的類別:UIImage+DSRoundImage.h
并在SDWebImage庫(kù)里處理image的時(shí)候使用類別方法繪制圓角并緩存。
使用Instruments的Core Animation查看性能
Color Offscreen-Rendered Yellow
開啟后會(huì)把那些需要離屏渲染的圖層高亮成黃色,這就意味著黃色圖層可能存在性能問題。
Color Hits Green and Misses Red
如果shouldRasterize被設(shè)置成YES,對(duì)應(yīng)的渲染結(jié)果會(huì)被緩存,如果圖層是綠色,就表示這些緩存被復(fù)用;如果是紅色就表示緩存會(huì)被重復(fù)創(chuàng)建,這就表示該處存在性能問題了。
用Instruments測(cè)試得
第一種方法,UIImageView和UIButton都高亮為黃色。
第二種方法,UIImageView和UIButton都高亮為綠色
第三種方法,無任何高亮,說明沒離屏渲染。
這種圓片覆蓋的方法一般只用在底色為純色的時(shí)候,如果圓角圖片的父View是張圖片的時(shí)候就沒辦法了,而且底色如果是多種顏色的話那要做多張不同顏色的圓片覆蓋。(可以用代碼取底色的顏色值給圓片著色)
第四種方法無任何高亮,說明沒離屏渲染(但是CPU消耗和內(nèi)存占用會(huì)很大)
第五種方法無任何高亮,說明沒離屏渲染,而且內(nèi)存占用也不大。(暫時(shí)感覺是最優(yōu)方法)
問題回復(fù):
有回復(fù)提到還有一種mask方法。
這種方法比第一種方法其實(shí)更卡頓。一次mask發(fā)生了兩次離屏渲染和一次主屏渲染。 具體可以參考小心別讓圓角成了你列表的幀數(shù)殺手
@nerozhao說第四種比第一種更卡。
我剛在demo里加了個(gè)例子測(cè)試了一下,第一種能明顯的感覺到卡頓,第四種還是挺順暢
的,有興趣的可以自己試試看。第四種是解決了離屏渲染GPU的問題。
可以用Instruments的 GPU Driver進(jìn)行測(cè)試:
Renderer Utilization
如果這個(gè)值超過了~50%,就意味著你的動(dòng)畫可能對(duì)幀率有所限制,很可能因?yàn)殡x屏渲染或者是重繪導(dǎo)致的過度混合。
Tiler Utilization
如果這個(gè)值超過了~50%,就意味著你的動(dòng)畫可能限制于幾何結(jié)構(gòu)方面,也就是在屏幕上有太多的圖層占用了。
Instruments
圖上面一部分是第一種方法的數(shù)據(jù),下面一部分是第四種方法的數(shù)據(jù)。
第一種方法的Renderer Utilization 和 Tiler Utilization 基本在90%左右。幀率在20左右。
第四種方法的Renderer Utilization 和 Tiler Utilization 基本在20%左右。幀率接近60。
幀率越接近60滑動(dòng)越順暢。
但是經(jīng)過跟@nerozhao的討論發(fā)現(xiàn)第四種Core Graphics繪制圓角會(huì)有大量的內(nèi)存占用,
而且每次繪制的時(shí)候CUP消耗會(huì)很大。
由于@nerozhao使用了UITableView進(jìn)行測(cè)試,因?yàn)閁ITableView滾動(dòng)的時(shí)候是一直在復(fù)用的,UIImageView會(huì)重復(fù)繪制,所以會(huì)一直消耗CUP,然后你就能看的明顯的卡頓。
@nerozhao在UITableView里圖片的繪制在后臺(tái)線程進(jìn)行繪制,解決了卡頓問題,但是由于是在后臺(tái)線程的異步繪制所以在滾動(dòng)的時(shí)候會(huì)看到圖片先是正方形然后再變成圓形。
而我使用的是UIScrollerView進(jìn)行的測(cè)試,只有第一次繪制的時(shí)候會(huì)占用CUP資源,所以滑動(dòng)的時(shí)候還是挺流暢的,但是內(nèi)存消耗還是很大。如果是主線程繪制的話會(huì)阻塞一點(diǎn)時(shí)間的主線程,而后臺(tái)線程繪制的話內(nèi)存消耗會(huì)更大,特別容易崩潰。
所以第四種方法當(dāng)圖片特別多的時(shí)候很容易R(shí)eceived memory warning導(dǎo)致崩潰
junyixie.github.io/2017/01/20/iOS-%E5%9B%BE%E5%83%8F%E7%BB%98%E5%88%B6/