iOS的離屏渲染

? ? ? ? ? 實(shí)際開發(fā)中壕探,大多會(huì)遇到圓角或者圓形的控件的情況琅催。通常圆丹,簡便的解決方案主要是:

1.讓美工做一個(gè)圓角的圖片滩愁,我們直接放圖片就OK。

2.就是常用的layer的那兩個(gè)屬性(cornerRadius , masksToBounds)辫封。

? ? ? ? ? ?第一種方法不說了硝枉,第二種方法,在圓角不多的時(shí)候還可以倦微,如果一個(gè)界面上的圓角控件很多的時(shí)候妻味,再用它就出問題了,欣福。就像下面這種情況的時(shí)候责球,滑動(dòng)tableView就會(huì)明顯感覺到屏幕的卡頓:


究其原因,我們在用masksToBounds這個(gè)屬性的時(shí)候GPU屏幕渲染才用了離屏渲染的方式拓劝。由此雏逾,我們引出了離屏渲染的概念------

? ? ? ? 離屏渲染是什么?

OpenGL中郑临,GPU屏幕渲染有以下兩種方式:

? ? ?1栖博、On-Screen Rendering:

? ? ? ?意思是當(dāng)前屏幕渲染,指的是GPU的渲染操作是在當(dāng)前用于顯示的屏幕緩沖區(qū)進(jìn)行厢洞。

? ? ?2仇让、Off-Screen Rendering:

? ? ? ?意思就是我們說的離屏渲染了,指的是GPU在當(dāng)前屏幕緩沖區(qū)以外新開辟一個(gè)緩沖區(qū)進(jìn)行渲染操作躺翻。


相比于當(dāng)前屏幕渲染丧叽,離屏渲染的代價(jià)是很高的,主要體現(xiàn)在兩個(gè)方面:

一获枝、創(chuàng)建新緩沖區(qū)蠢正,要想進(jìn)行離屏渲染,首先要?jiǎng)?chuàng)建一個(gè)新的緩沖區(qū)省店。

二嚣崭、上下文切換,離屏渲染的整個(gè)過程懦傍,需要多次切換上下文環(huán)境:先是從當(dāng)前屏幕(On-Screen)切換到離屏(Off-Screen)雹舀;等到離屏渲染結(jié)束以后,將離屏緩沖區(qū)的渲染結(jié)果顯示到屏幕上有需要將上下文環(huán)境從離屏切換到當(dāng)前屏幕粗俱。而上下文環(huán)境的切換是要付出很大代價(jià)的说榆。

會(huì)導(dǎo)致離屏渲染的幾種情況:

1. custom drawRect: (any, even if you simply fill the background with color)

2. CALayer shadow

3. CALayer mask

4. any custom drawing using CGContext

解決圓角離屏渲染的方案:

圓角使用UIImageView裝載一個(gè)圓角image來處理,簡單地說就是比如一個(gè)UIView(或其子類)設(shè)置圓角,就在底層鋪一個(gè)UIImageView,然后用GraphicsContext生成一張帶圓角的圖片放在UIImageView上签财。

#import "DrCorner.h"

@interface DrCorner()

@end

@implementation DrCorner

+ (CGFloat)ceilbyunit:(CGFloat)num unit:(double)unit {

return num -modf(num, &unit) + unit;

}

+ (CGFloat)floorbyunit:(CGFloat)num unit:(double)unit {

return num -modf(num, &unit);

}

+ (CGFloat)roundbyunit:(CGFloat)num unit:(double)unit {

CGFloat remain =modf(num, &unit);

if (remain > unit /2.0) {

return [self ceilbyunit:num unit:unit];

}else{

return [self floorbyunit:num unit:unit];

}

}

+ (CGFloat)pixel:(CGFloat)num {

CGFloat unit;

CGFloat scale = [[UIScreen mainScreen] scale];

switch((NSInteger)scale) {

case 1:

unit =1.0/1.0;

break;

case 2:

unit =1.0/2.0;

break;

case 3:

unit =1.0/3.0;

break;

default:

unit =0.0;

break;

}

return [self roundbyunit:num unit:unit];

}

@end

- (void)dr_addCornerRadius:(CGFloat)radius

borderWidth:(CGFloat)borderWidth

backgroundColor:(UIColor*)backgroundColor

borderCorlor:(UIColor*)borderColor {

UIImageView *imageView = [[UIImage Viewalloc] initWithImage:[self? dr_drawRectWithRoundedCornerRadius:radius

?borderWidth:borderWidth

backgroundColor:backgroundColor

borderCorlor:borderColor]];

[self insertSubview:imageView atIndex:0];

}

- (UIImage*)dr_drawRectWithRoundedCornerRadius:(CGFloat)radius

borderWidth:(CGFloat)borderWidth

backgroundColor:(UIColor*)backgroundColor

borderCorlor:(UIColor*)borderColor {

CGSizesizeToFit =CGSizeMake([DrCorner pixel:self.bounds.size.width],self.bounds.size.height);

CGFloat halfBorderWidth = borderWidth /2.0;

UIGraphicsBeginImageContextWithOptions(sizeToFit,NO, [UIScreen mainScreen].scale);

CGContextRefcontext =UIGraphicsGetCurrentContext();

CGContextSetLineWidth(context, borderWidth);

CGContextSetStrokeColorWithColor(context, borderColor.CGColor);

CGContextSetFillColorWithColor(context, backgroundColor.CGColor);

CGFloatwidth = sizeToFit.width, height = sizeToFit.height;

CGContextMoveToPoint(context, width - halfBorderWidth, radius + halfBorderWidth);//準(zhǔn)備開始移動(dòng)坐標(biāo)

CGContextAddArcToPoint(context, width - halfBorderWidth, height - halfBorderWidth, width - radius - halfBorderWidth, height - halfBorderWidth, radius);

CGContextAddArcToPoint(context, halfBorderWidth, height - halfBorderWidth, halfBorderWidth, height - radius - halfBorderWidth, radius);//左下角角度

CGContextAddArcToPoint(context, halfBorderWidth, halfBorderWidth, width - halfBorderWidth, halfBorderWidth, radius);//左上角

CGContextAddArcToPoint(context, width - halfBorderWidth, halfBorderWidth, width - halfBorderWidth, radius + halfBorderWidth, radius);

CGContextDrawPath(UIGraphicsGetCurrentContext(),kCGPathFillStroke);

UIImage*image =UIGraphicsGetImageFromCurrentImageContext();

UIGraphicsEndImageContext();

returnimage;

}

給一個(gè)圖片做出圓角:

@implementationUIImageView (CornerRounder)

- (void)dr_addCornerRadius:(CGFloat)radius {

self.image= [self.image dr_imageAddCornerWithRadius:radius andSize:self.bounds.size];

}

@end

@implementationUIImage (ImageCornerRounder)

- (UIImage*)dr_imageAddCornerWithRadius:(CGFloat)radius andSize:(CGSize)size{

CGRect rect =CGRectMake(0,0, size.width, size.height);

UIGraphicsBeginImageContextWithOptions(size,NO, [UIScreen mainScreen].scale);

CGContextRefctx =UIGraphicsGetCurrentContext();

UIBezierPath* path = [UIBezierPath bezierPathWithRoundedRect:rect byRoundingCorners:UIRectCornerAllCorners cornerRadii:CGSizeMake(radius, radius)];

CGContextAddPath(ctx,path.CGPath);

CGContextClip(ctx);

[self drawInRect:rect];

CGContextDrawPath(ctx,kCGPathFillStroke);

UIImage* newImage =UIGraphicsGetImageFromCurrentImageContext();

UIGraphicsEndImageContext();

returnnewImage;

}

@end

這樣就可以就可以有效避免大量的離屏渲染拖慢fps串慰。

其中 CGContextAddArcToPoint用法:

void CGContextAddArcToPoint(CGContextRef c,CGFloatx1,CGFloaty1,CGFloatx2,CGFloaty2,CGFloatradius)

下圖中,P1 是當(dāng)前路徑所在的點(diǎn),坐標(biāo)是(x,y)

P1(x,y)和(x1,y1)構(gòu)成切線1,(x1,y1)和(x2,y2)構(gòu)成切線2, r 是上面函數(shù)中的radius, 紅色的線就是CGContextAddArcToPoint繪制的曲線. 它不會(huì)畫到 (x2, y2)這個(gè)點(diǎn)唱蒸, 繪制到圓弧的終點(diǎn)就會(huì)停止.?



當(dāng)然邦鲫,在圓角較少的情況下大可不必這么折騰,設(shè)置圓角的方法也有很多種神汹,除了最常用的layer兩個(gè)屬性以外庆捺,還有:

1.UIBezierPath:

- (void)drawRect:(CGRect)rect {

CGRect bounds =self.bounds;

[[UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:8.

0] addClip];

[self.image drawInRect:bounds];

}

2.maskLayer(CAShapeLayer)

- (void)drawRect:(CGRect)rect {

? UIBezierPath *maskPatch = [UIBezierPath bezierPathWithRoundedRect:rect byRoundingCorners:UIRectCornerTopLeft | UIRectCornerTopRight cornerRadii:CGSizeMake(10, 10)]; //這里的第二個(gè)參數(shù)可以設(shè)置圓角的位置,這里是設(shè)置左上和右上兩個(gè)圓角? ?

?CAShapeLayer *maskLayer = [[CAShapeLayer alloc]init];??

? maskLayer.frame = self.bounds;??

? maskLayer.path = maskPatch.CGPath;? ?

? self.layer.mask = maskLayer;

}

這里要感謝葉孤城大神的文章解疑屁魏,本文圓角設(shè)置方法介紹不算全面滔以,還有待補(bǔ)充,只是最近項(xiàng)目上遇到了百年一遇的離屏渲染導(dǎo)致了嚴(yán)重卡屏問題所以就小研究了一下氓拼,并做下筆記你画。

----------------------------華麗的時(shí)間分割線------------------------------

2月15日補(bǔ)充:陰影效果的離屏渲染

給UIView及其子類添加陰影效果的時(shí)候,通常我們是這么做的:

//陰影的顏色

self.imageView.layer.shadowColor= [UIColor blackColor].CGColor;

//陰影的透明度

self.imageView.layer.shadowOpacity=0.8f;

//陰影的圓角

self.imageView.layer.shadowRadius=4;

//陰影偏移量

self.imageView.layer.shadowOffset=CGSizeMake(0,0);

這里就像上文所說的桃漾,直接設(shè)置了shadowOffset撬即,因此導(dǎo)致了離屏渲染。

解決辦法:

用shadowPath代替呈队。

self.imageView.layer.shadowPath = CGPathCreateWithRect(self.imageView.layer.bounds, nil);

或者可以自定義路徑陰影

UIBezierPath*path = [UIBezierPath bezierPath];

[path moveToPoint:CGPointMake(-5, -5)];

//添加直線

[path addLineToPoint:CGPointMake(imageWidth /2, -15)];

[path addLineToPoint:CGPointMake(imageWidth +5, -5)];

[path addLineToPoint:CGPointMake(imageWidth +15, imageHeight /2)];

[path addLineToPoint:CGPointMake(imageWidth +5, imageHeight +5)];

[path addLineToPoint:CGPointMake(imageWidth /2, imageHeight +15)];

[path addLineToPoint:CGPointMake(-5, imageHeight +5)];

[path addLineToPoint:CGPointMake(-15, imageHeight /2)];

[path addLineToPoint:CGPointMake(-5, -5)];

//設(shè)置陰影路徑

self.imageView.layer.shadowPath= path.CGPath;

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市唱歧,隨后出現(xiàn)的幾起案子宪摧,更是在濱河造成了極大的恐慌询枚,老刑警劉巖炸客,帶你破解...
    沈念sama閱讀 206,214評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異筹陵,居然都是意外死亡沿后,警方通過查閱死者的電腦和手機(jī)沿彭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來尖滚,“玉大人喉刘,你說我怎么就攤上這事∑崤” “怎么了睦裳?”我有些...
    開封第一講書人閱讀 152,543評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長撼唾。 經(jīng)常有香客問我廉邑,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,221評(píng)論 1 279
  • 正文 為了忘掉前任蛛蒙,我火速辦了婚禮糙箍,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘牵祟。我一直安慰自己深夯,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評(píng)論 5 371
  • 文/花漫 我一把揭開白布课舍。 她就那樣靜靜地躺著塌西,像睡著了一般。 火紅的嫁衣襯著肌膚如雪筝尾。 梳的紋絲不亂的頭發(fā)上捡需,一...
    開封第一講書人閱讀 49,007評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音筹淫,去河邊找鬼站辉。 笑死,一個(gè)胖子當(dāng)著我的面吹牛损姜,可吹牛的內(nèi)容都是我干的饰剥。 我是一名探鬼主播,決...
    沈念sama閱讀 38,313評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼摧阅,長吁一口氣:“原來是場噩夢啊……” “哼汰蓉!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起棒卷,我...
    開封第一講書人閱讀 36,956評(píng)論 0 259
  • 序言:老撾萬榮一對情侶失蹤顾孽,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后比规,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體若厚,經(jīng)...
    沈念sama閱讀 43,441評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評(píng)論 2 323
  • 正文 我和宋清朗相戀三年蜒什,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了测秸。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,018評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡灾常,死狀恐怖霎冯,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情岗憋,我是刑警寧澤肃晚,帶...
    沈念sama閱讀 33,685評(píng)論 4 322
  • 正文 年R本政府宣布,位于F島的核電站仔戈,受9級(jí)特大地震影響关串,放射性物質(zhì)發(fā)生泄漏拧廊。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評(píng)論 3 307
  • 文/蒙蒙 一晋修、第九天 我趴在偏房一處隱蔽的房頂上張望吧碾。 院中可真熱鬧,春花似錦墓卦、人聲如沸倦春。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽睁本。三九已至,卻和暖如春忠怖,著一層夾襖步出監(jiān)牢的瞬間呢堰,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評(píng)論 1 261
  • 我被黑心中介騙來泰國打工凡泣, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留枉疼,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,467評(píng)論 2 352
  • 正文 我出身青樓鞋拟,卻偏偏與公主長得像骂维,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子贺纲,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • 前言:關(guān)于貝塞爾曲線與CAShapeLayer的學(xué)習(xí) 學(xué)習(xí)Demo演示: 貝塞爾曲線簡單了解 使用UIBezier...
    麥穗0615閱讀 17,857評(píng)論 18 149
  • 在iOS中隨處都可以看到絢麗的動(dòng)畫效果航闺,實(shí)現(xiàn)這些動(dòng)畫的過程并不復(fù)雜,今天將帶大家一窺ios動(dòng)畫全貌猴誊。在這里你可以看...
    每天刷兩次牙閱讀 8,465評(píng)論 6 30
  • -------》(轉(zhuǎn)發(fā)) 這篇文章介紹ZYCornerRadius解決生產(chǎn)中圓角帶來的離屏渲染問題的思路来颤。 日常生...
    每天刷兩次牙閱讀 827評(píng)論 0 0
  • 在iOS中隨處都可以看到絢麗的動(dòng)畫效果,實(shí)現(xiàn)這些動(dòng)畫的過程并不復(fù)雜稠肘,今天將帶大家一窺iOS動(dòng)畫全貌。在這里你可以看...
    F麥子閱讀 5,094評(píng)論 5 13
  • 本文是基于UIKit性能調(diào)優(yōu)實(shí)戰(zhàn)講解 - 簡書這篇文章的總結(jié)詳細(xì)請看原文 實(shí)例講解可以參考:小心別讓圓角成了你列表...
    Www劉閱讀 1,012評(píng)論 1 8