什么是離屏渲染涵紊?
離屏渲染就是在屏幕之外渲染圖形圖像哈街,不會(huì)直接顯示到屏幕上揣苏,等待合適的時(shí)機(jī)再顯示悯嗓。
什么情況下會(huì)觸發(fā)離屏渲染?
說(shuō)幾種iOS開發(fā)的過(guò)程中常見的離屏渲染:
1.?使用了?mask?的?layer (layer.mask)
2. 需要進(jìn)行裁剪的?layer (layer.masksToBounds / view.clipsToBounds)
3. ?設(shè)置了組透明度為?YES卸察,并且透明度不為?1?的?layer (layer.allowsGroupOpacity/ layer.opacity)
4. 添加了投影的?layer (layer.shadow*)
5. 采用了光柵化的?layer (layer.shouldRasterize)
6. 繪制了文字的?layer (UILabel, CATextLayer, Core Text?等)
為什么會(huì)觸發(fā)離屏渲染脯厨?
當(dāng)app在渲染的過(guò)程中需要使用額外的渲染或者合并的時(shí)候就會(huì)觸發(fā)離屏渲染。
比如我們?cè)赨IImageView上加了一個(gè)mask:
由于UIImageView本身就存在一個(gè)layer坑质,所以這就是上面提到的第一種會(huì)觸發(fā)離屏渲染的情況合武。在渲染的時(shí)候就需要先渲染我們?cè)嫉膱D片,將渲染結(jié)果放到離屏緩沖區(qū)(offscreen buffer)里面涡扼,再渲染mask圖層稼跳,也將其結(jié)果存儲(chǔ)到離屏緩沖區(qū)里,最后將這兩個(gè)結(jié)果混合渲染的結(jié)果放到幀緩沖區(qū)里面壳澳,在下一次runloop到來(lái)的時(shí)候?qū)⑵滹@示到屏幕上岂贩。
離屏渲染會(huì)帶來(lái)什么問(wèn)題?
1巷波、離屏渲染需要額外的存儲(chǔ)空間萎津,它需要存儲(chǔ)一些渲染過(guò)程中的一些中間的結(jié)果。而我們的離屏緩沖區(qū)是有大小限制的抹镊,在蘋果關(guān)于光柵化(shouldRasterize)解釋了離屏緩沖區(qū)的大小為2.5倍屏幕大小锉屈。超出其大小的結(jié)果會(huì)被丟棄。
2垮耳、會(huì)帶來(lái)性能問(wèn)題颈渊,最常見的就是掉幀問(wèn)題(iOS中的掉幀問(wèn)題)遂黍。
為什么要使用離屏渲染?
既然離屏渲染會(huì)帶來(lái)各種各樣的問(wèn)題俊嗽,我們?yōu)槭裁催€要用離屏渲染呢雾家?
1、一些特殊的效果绍豁,不得不使用離屏渲染芯咧。比如 使用系統(tǒng)的毛玻璃(UIVisualEffectView)效果,使用圓角竹揍、陰影等敬飒,它們都是由系統(tǒng)自動(dòng)觸發(fā)的離屏渲染。
2芬位、效率優(yōu)勢(shì)无拗,有些效果我們需要多次頻繁的使用,就可以提前準(zhǔn)備在離屏緩沖區(qū)(offscreen Buffer)里面昧碉,從而達(dá)到復(fù)用的目的英染,是渲染速度更快捷。
如何避免離屏渲染晌纫?
1税迷、減少使用有透明度疊加的效果永丝。
2锹漱、使用圓角的時(shí)候不是系統(tǒng)自帶的,自己實(shí)現(xiàn)圓角效果(比如使用貝塞爾曲線)
注:在UIView里面使用layer.cornerRadius并不會(huì)直接觸發(fā)離屏渲染慕嚷,它只會(huì)對(duì)view的背景哥牍、邊框進(jìn)行圓角。
以上就是我對(duì)離屏渲染的理解喝检,最后附上YYImage對(duì)圓角的處理(YYImage源碼地址):
- (UIImage *)yy_imageByRoundCornerRadius:(CGFloat)radius corners:(UIRectCorner)corners
borderWidth:(CGFloat)borderWidth
borderColor:(UIColor *)borderColor borderLineJoin:(CGLineJoin)borderLineJoin {
if (corners != UIRectCornerAllCorners) {
UIRectCorner tmp = 0;
if (corners & UIRectCornerTopLeft) tmp |= UIRectCornerBottomLeft;
if (corners & UIRectCornerTopRight) tmp |= UIRectCornerBottomRight; if (corners & UIRectCornerBottomLeft) tmp |= UIRectCornerTopLeft;
if (corners & UIRectCornerBottomRight) tmp |= UIRectCornerTopRight; corners = tmp;
}
UIGraphicsBeginImageContextWithOptions(self.size, NO, self.scale); CGContextRef context = UIGraphicsGetCurrentContext();
CGRect rect = CGRectMake(0, 0, self.size.width, self.size.height); CGContextScaleCTM(context, 1, -1); CGContextTranslateCTM(context, 0, -rect.size.height);
CGFloat minSize = MIN(self.size.width, self.size.height); if (borderWidth < minSize / 2) {
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectInset(rect, borderWidth, borderWidth) byRoundingCorners:corners cornerRadii:CGSizeMake(radius, borderWidth)];
[path closePath];
CGContextSaveGState(context);
[path addClip];
CGContextDrawImage(context, rect, self.CGImage); CGContextRestoreGState(context);
}
if (borderColor && borderWidth < minSize / 2 && borderWidth > 0) {
CGFloat strokeInset = (floor(borderWidth * self.scale) + 0.5) / self.scale;
CGRect strokeRect = CGRectInset(rect, strokeInset, strokeInset);
CGFloat strokeRadius = radius > self.scale / 2 ? radius - self.scale / 2 : 0;
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:strokeRect byRoundingCorners:corners cornerRadii:CGSizeMake(strokeRadius,
borderWidth)];
[path closePath];
path.lineWidth = borderWidth; path.lineJoinStyle = borderLineJoin; [borderColor setStroke];