欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

iOS设置圆角阴影 避免离屏渲染

程序员文章站 2023-11-30 08:21:16
ios 控件设置圆角,避免离屏渲染。 离屏渲染:指gpu(图形处理器)在当前屏幕缓冲区外新开辟一个渲染缓冲区进行工作。这会给我们带来额外的性能损耗,如果这样的操作达到一定...

ios 控件设置圆角,避免离屏渲染。

离屏渲染:指gpu(图形处理器)在当前屏幕缓冲区外新开辟一个渲染缓冲区进行工作。这会给我们带来额外的性能损耗,如果这样的操作达到一定数量,会触发缓冲区的频繁合并和上下文的的频繁切换,会出现卡顿、掉帧现象。造成离屏渲染的原因有很多,如:shouldrasterize(光栅化)、mask(遮罩层)、shadows(阴影)、edgeanntialiasing(抗锯齿)、cornerradius(圆角)等等。

下面说一下什么情况下设置圆角会造成离屏渲染:

//设置cornerradius>0且maskstobounds为yes
view.layer.cornerradius = 10.f;
view.layer.maskstobounds = yes;
 
//设置cornerradius>0且maskstobounds为yes
view.layer.cornerradius = 10.f;
view.cliptobounds = yes;
 
//像下面设置view.layer.mask
uibezierpath *path = [uibezierpath bezierpathwithroundedrect:imgview.bounds byroundingcorners:uirectcornerallcorners cornerradii:cgsizemake(10.f, 10.f)];
cashapelayer *masklayer = [[cashapelayer alloc] init];
masklayer.frame = view.bounds;
masklayer.path = path.cgpath;
view.layer.mask = masklayer;

上面第一种应该是最常用的,是在设置了圆角及maskstobounds为yes时,才会触发离屏渲染,而maskstobounds默认是no,也就是说只要不设置这个属性就能避免很多情况了,下面说一下如何切一个不触发离屏渲染的圆角:

uiview、uitextfield、uitextview等大部分控件都可以像下面这样设置:

view.layer.cornerradius = 10.f;
view.layer.maskstobounds = no;

有一些特殊情况,uilabel设置时,不要设置label.backgroundcolor,应设置:

label.layer.cornerradius = 10.f;
label.layer.backgroundcolor = [uicolor whitecolor].cgcolor;

有图片的uibutton、uiimageview,用drawinrect绘制uiimage圆角:

uibezierpath *bezierpath = [uibezierpath bezierpathwithroundedrect:rect cornerradius:cornerradius];
uigraphicsbeginimagecontextwithoptions(rect.size, false, [uiscreen mainscreen].scale);
cgcontextaddpath(uigraphicsgetcurrentcontext(), bezierpath.cgpath);
cgcontextclip(uigraphicsgetcurrentcontext());
[self drawinrect:rect];
 
cgcontextdrawpath(uigraphicsgetcurrentcontext(), kcgpathfillstroke);
uiimage *image = uigraphicsgetimagefromcurrentimagecontext();
uigraphicsendimagecontext();

查看离屏渲染,模拟器可以选中“debug - color off-screen rendered”开启调试,真机可以用instruments检测,“instruments - core animation - debug options - color offscreen-rendered yellow”开启调试,开启后,有离屏渲染的图层会变成高亮的黄色。

写了个离屏渲染的样例:

iOS设置圆角阴影 避免离屏渲染

下面贴上代码:

控制器viewcontroller:

#import "viewcontroller.h"
#import "uiimage+hwcorner.h"
 
#define kmainw [uiscreen mainscreen].bounds.size.width
 
@interface viewcontroller ()
 
@end
 
@implementation viewcontroller
 
- (void)viewdidload {
 [super viewdidload];
 
 self.view.backgroundcolor = [uicolor whitecolor];
 
 //创建控件
 [self creatcontrol];
}
 
- (void)creatcontrol
{
 cgfloat margin = 20.f;
 cgfloat controlw = (kmainw - margin * 3) * 0.5;
 
 nsarray *titlearray = @[@"离屏渲染", @"非离屏渲染"];
 for (int i = 0; i < titlearray.count; i++) {
 cgfloat controlx = margin + (controlw + margin) * i;
 //uilabel
 uilabel *label = [[uilabel alloc] initwithframe:cgrectmake(controlx, 30, controlw, 40)];
 label.layer.backgroundcolor = [[uicolor graycolor] cgcolor];
 label.text = titlearray[i];
 label.textalignment = nstextalignmentcenter;
 label.layer.cornerradius = label.bounds.size.height * 0.5;
 label.layer.maskstobounds = i == 0 ? yes : no;
 [self.view addsubview:label];
 
 //uiview
 uiview *view = [[uiview alloc] initwithframe:cgrectmake(controlx, cgrectgetmaxy(label.frame) + margin, controlw, 40)];
 view.backgroundcolor = [uicolor graycolor];
 view.layer.cornerradius = view.bounds.size.height * 0.5;
 view.layer.maskstobounds = i == 0 ? yes : no;
 [self.view addsubview:view];
 
 //uiview若未添加子控件,设置view.layer.maskstobounds = yes;也不会造成离屏渲染
 uiview *subview = [[uiview alloc] initwithframe:cgrectmake(30, 0, controlw - 60, 40)];
 subview.backgroundcolor = [uicolor redcolor];
 [view addsubview:subview];
 
 //uitextview
 uitextview *textview = [[uitextview alloc] initwithframe:cgrectmake(controlx, cgrectgetmaxy(view.frame) + margin, controlw, 40)];
 textview.userinteractionenabled = no;
 textview.backgroundcolor = [uicolor graycolor];
 if (i == 0) {
  /*
  这里换了一种实现方法,用uibezierpath赋值layer.mask,两种方式都会造成离屏渲染
  textview.layer.cornerradius = textview.bounds.size.height * 0.5;
  textview.layer.maskstobounds = yes;
  */
  uibezierpath *path = [uibezierpath bezierpathwithroundedrect:textview.bounds byroundingcorners:uirectcornertopleft | uirectcornertopright cornerradii:cgsizemake(textview.bounds.size.height * 0.5, textview.bounds.size.height * 0.5)];
  cashapelayer *masklayer = [[cashapelayer alloc] init];
  masklayer.frame = textview.bounds;
  masklayer.path = path.cgpath;
  textview.layer.mask = masklayer;
 }else {
  textview.layer.cornerradius = textview.bounds.size.height * 0.5;
  textview.layer.maskstobounds = no;
 }
 [self.view addsubview:textview];
 
 //uibutton
 uibutton *button = [[uibutton alloc] initwithframe:cgrectmake(controlx, cgrectgetmaxy(textview.frame) + margin, controlw, controlw)];
 if (i == 0) {
  [button setimage:[uiimage imagenamed:@"hero_1"] forstate:uicontrolstatenormal];
  button.layer.cornerradius = button.bounds.size.width * 0.5;
  button.layer.maskstobounds = yes;
 }else {
  [button setimage:[[uiimage imagenamed:@"hero_1"] drawcornerinrect:button.bounds cornerradius:button.bounds.size.width * 0.5] forstate:uicontrolstatenormal];
 }
 [self.view addsubview:button];
 
 //uiimageview设置圆角
 uiimageview *imageview = [[uiimageview alloc] initwithframe:cgrectmake(controlx, cgrectgetmaxy(button.frame) + margin, controlw, controlw)];
 if (i == 0) {
  [imageview setimage:[uiimage imagenamed:@"hero_1"]];
  imageview.layer.cornerradius = imageview.bounds.size.width * 0.5;
  imageview.layer.maskstobounds = yes;
 }else {
  [imageview setimage:[[uiimage imagenamed:@"hero_1"] drawcornerinrect:imageview.bounds cornerradius:imageview.bounds.size.width * 0.5]];
 }
 [self.view addsubview:imageview];
 
 //uiimageview若未添加子控件,设置imageview.layer.maskstobounds = yes;也不会造成离屏渲染
 uiview *subimageview = [[uiview alloc] initwithframe:cgrectmake(0, 0, controlw, 40)];
 subimageview.backgroundcolor = [uicolor redcolor];
 subimageview.layer.cornerradius = imageview.bounds.size.width * 0.5;
 [imageview addsubview:subimageview];
 
 //uiimageview设置阴影
 cgfloat imgw = 70.f;
 cgfloat imgpadding = (kmainw - imgw * 4 - margin * 2) / 3;
 uiimageview *shadowimgview = [[uiimageview alloc] initwithframe:cgrectmake(margin + (imgw + imgpadding) * 2 * i, cgrectgetmaxy(imageview.frame) + margin, imgw, imgw)];
 [shadowimgview setimage:[uiimage imagenamed:@"hero_1"]];
 shadowimgview.layer.shadowcolor = [uicolor redcolor].cgcolor;
 shadowimgview.layer.shadowopacity = 0.8f;
 shadowimgview.layer.shadowoffset = cgsizemake(5, 5);
 shadowimgview.layer.shadowradius = 5.f;
 if (i == 1) {
  uibezierpath *path = [uibezierpath bezierpathwithrect:shadowimgview.bounds];
  shadowimgview.layer.shadowpath = path.cgpath;
 }
 [self.view addsubview:shadowimgview];
 
 //uiimageview设置阴影+圆角
 uiimageview *shadowcorimgview = [[uiimageview alloc] initwithframe:cgrectmake(margin + imgw + imgpadding + (imgw + imgpadding) * 2 * i, cgrectgetminy(shadowimgview.frame), imgw, imgw)];
 [shadowcorimgview setimage:[[uiimage imagenamed:@"hero_1"] drawcornerinrect:imageview.bounds cornerradius:imageview.bounds.size.width * 0.5]];
 shadowcorimgview.layer.shadowcolor = [uicolor redcolor].cgcolor;
 shadowcorimgview.layer.shadowopacity = 0.8f;
 shadowcorimgview.layer.shadowoffset = cgsizemake(0, 0);
 shadowcorimgview.layer.shadowradius = 5.f;
 if (i == 1) {
  uibezierpath *path = [uibezierpath bezierpathwithroundedrect:shadowcorimgview.bounds cornerradius:shadowcorimgview.bounds.size.height * 0.5];
  shadowcorimgview.layer.shadowpath = path.cgpath;
 }
 [self.view addsubview:shadowcorimgview];
 }
}
 
@end

uiimage分类,uiimage+hwcorner:

#import <uikit/uikit.h>
 
@interface uiimage (hwcorner)
 
//绘制图片圆角
- (uiimage *)drawcornerinrect:(cgrect)rect cornerradius:(cgfloat)cornerradius;
 
@end
 
/*** ---------------分割线--------------- ***/ 
 
#import "uiimage+hwcorner.h"
 
@implementation uiimage (hwcorner)
 
//绘制图片圆角
- (uiimage *)drawcornerinrect:(cgrect)rect cornerradius:(cgfloat)cornerradius
{
 uibezierpath *bezierpath = [uibezierpath bezierpathwithroundedrect:rect cornerradius:cornerradius];
 uigraphicsbeginimagecontextwithoptions(rect.size, false, [uiscreen mainscreen].scale);
 cgcontextaddpath(uigraphicsgetcurrentcontext(), bezierpath.cgpath);
 cgcontextclip(uigraphicsgetcurrentcontext());
 [self drawinrect:rect];
 
 cgcontextdrawpath(uigraphicsgetcurrentcontext(), kcgpathfillstroke);
 uiimage *image = uigraphicsgetimagefromcurrentimagecontext();
 uigraphicsendimagecontext();
 
 return image;
}
 
@end

最后说一下,只有在大量的离屏渲染情况时才会出现卡顿、掉帧等现象,没必要过分追求容不下一丝黄色。性能上instruments工具还可以检测很多。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。