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

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”开启调试,开启后,有离屏渲染的图层会变成高亮的黄色。

写了个离屏渲染的样例:

下面贴上代码:

控制器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工具还可以检测很多。

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

(0)

相关推荐

  • iOS如何为圆角添加阴影效果示例代码

    前言 大家都知道在iOS中为UIView添加阴影还是比较简单的,只需要设置layer的shadow属性就可以了,但是问题在于设置阴影之后,必须设置masksToBounds为NO,而圆角图片则要求masksToBounds必须为YES,两者相互冲突,会导致无法正确的添加阴影.下面就来给大家介绍正确为圆角添加阴影的效果,话不多说了,来一起看看详细的介绍吧. 先来看看效果图: 正确的做法: 先创建一个透明的UIView,并添加阴影,设置masksToBounds为NO: 然后在透明的UIView上添

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

    iOS 控件设置圆角,避免离屏渲染. 离屏渲染:指GPU(图形处理器)在当前屏幕缓冲区外新开辟一个渲染缓冲区进行工作.这会给我们带来额外的性能损耗,如果这样的操作达到一定数量,会触发缓冲区的频繁合并和上下文的的频繁切换,会出现卡顿.掉帧现象.造成离屏渲染的原因有很多,如:shouldRasterize(光栅化).mask(遮罩层).shadows(阴影).EdgeAnntialiasing(抗锯齿).cornerRadius(圆角)等等. 下面说一下什么情况下设置圆角会造成离屏渲染: //设置c

  • iOS设置圆角的三种方法

    本文实例为大家分享了iOS设置圆角的三种方式,供大家参考,具体内容如下 第一种方法:通过设置layer的属性 最简单的一种,但是很影响性能,一般在正常的开发中使用很少. UIImageView *imageView = [[UIImageView alloc]initWithFrame:CGRectMake(100, 100, 100, 100)]; //只需要设置layer层的两个属性 //设置圆角 imageView.layer.cornerRadius = imageView.frame.

  • iOS设置圆角的三种方式

    第一种方法:通过设置layer的属性 最简单的一种,但是很影响性能,一般在正常的开发中使用很少. UIImageView *imageView = [[UIImageView alloc]initWithFrame:CGRectMake(100, 100, 100, 100)]; //只需要设置layer层的两个属性 //设置圆角 imageView.layer.cornerRadius = imageView.frame.size.width / 2; //将多余的部分切掉 imageView

  • iOS设置圆角的4种方法实例(附性能评测)

    四种设置圆角的方法 从网上收集了各种设置圆角的方法,总结起来有以下四种: 1.设置 layer 的 cornerRadius view.layer.masksToBounds = YES; view.layer.cornerRadius = imgSize.width / 2; 2.用贝塞尔曲线作 mask 圆角 CAShapeLayer *layer = [CAShapeLayer layer]; UIBezierPath *aPath = [UIBezierPath bezierPathWi

  • IOS 性能优化中离屏渲染

    GPU屏幕渲染有以下两种方式: On-Screen Rendering 意为当前屏幕渲染,指的是GPU的渲染操作是在当前用于显示的屏幕缓冲区中进行. Off-Screen Rendering 意为离屏渲染,指的是GPU在当前屏幕缓冲区以外新开辟一个缓冲区进行渲染操作. 特殊的离屏渲染: 如果将不在GPU的当前屏幕缓冲区中进行的渲染都称为离屏渲染,那么就还有另一种特殊的"离屏渲染"方式: CPU渲染. 如果我们重写了drawRect方法,并且使用任何Core Graphics的技术进行了

  • iOS中设置圆角的几种方法示例

    前言 圆角(RounderCorner)是一种很常见的视图效果,相比于直角,它更加柔和优美,易于接受.但很多人并不清楚如何设置圆角的正确方式和原理.设置圆角会带来一定的性能损耗,如何提高性能是另一个需要重点讨论的话题.我查阅了一些现有的资料,收获良多的同时也发现了一些误导人错误. 1. 使用layer属性 layer.backgroundColor = [UIColor cyanColor].CGColor; // 给图层添加背景色 layer.contents = (id)[UIImage i

  • IOS设置按钮为圆角的示例代码

    iOS中很多时候都需要用到指定风格的圆角按钮,以下是UIButton提供的创建圆角按钮方法 设置按钮的4个角: 左上:UIRectCornerTopLeft 左下:UIRectCornerBottomLeft 右上:UIRectCornerTopRight 右下:UIRectCornerBottomRight 示例代码: UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(50, 60, 80, 40)]; button.b

  • iOS设置可选择圆角方向的控件圆角

    前言 这篇文章主要给大家介绍利用iOS如何设置可选择圆角方向的控件圆角,话不多说,以下是实现的示例代码,一起来看看吧. 示例代码 一.通过设置控件layer的cornerRadius来设置圆角 self.view.layer.cornerRadius =10.f;//如果设置圆角角度为半圆,则数值为控件高度的一半 self.view.layer.masksToBounds = YES;//是否删除多余的位置 二.通过贝塞尔曲线来设置圆角 UIBezierPath *maskPath = [UIB

  • IOS设置UIView的边框为圆角详解及实例

    IOS设置UIView的边框为圆角 iOS 系统自带的 View 组件都是正方形的,看起来都太生硬,有时候我需要变成圆角形式,如下图: 具体的实现是使用QuartzCore库,下面我具体的描述一下实现过程: •    首先创建一个项目,名字叫:ipad_webwiew     •    利用Interface Builder添加一个UIWebView,然后和相应的代码相关联     •    添加QuartzCore.framework 代码实现: 头文件: #import <UIKit/UIK

  • iOS 设置View阴影效果

    需要设置 颜色 阴影半径 等元素 UIView *shadowView = [[UIView alloc] init]; shadowView.frame = CGRectMake(100, 100, 100, 100); shadowView.center = self.view.center; shadowView.backgroundColor = [UIColor whiteColor]; //设置阴影颜色 shadowView.layer.shadowColor = [UIColor

随机推荐