iOS圆角的离屏渲染,你真的弄明白了吗

测试环境

Xcode 11.5
iPhone 11 Pro Simulator
iOS 13.5

  1. 如何设置圆角才会触发离屏渲染

我们经常看到,圆角会触发离屏渲染。但其实这个说法是不准确的,因为圆角触发离屏渲染也是有条件的!
我们先来看看苹果官方文档对于cornerRadius的描述:

Setting the radius to a value greater than 0.0 causes the layer to begin drawing rounded corners on its background. By default, the corner radius does not apply to the image in the layer’s contents property; it applies only to the background color and border of the layer. However, setting the masksToBounds property to true causes the content to be clipped to the rounded corners.

我们发现设置cornerRadius大于0时,只为layer的backgroundColor和border设置圆角;而不会对layer的contents设置圆角,除非同时设置了layer.masksToBounds为true(对应UIView的clipsToBounds属性)。

如果这时,你认为layer.masksToBounds或者clipsToBounds设置为true就会触发离屏渲染,这是不完全正确的。

我们先打开模拟器的离屏渲染颜色标记:

  • 不设置layer.masksToBounds或者clipsToBounds,其默认值为NO
- (void)viewDidLoad {
    [super viewDidLoad];
    UIView *view1 = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 200.0, 200.0)];
    // 设置背景色
    view1.backgroundColor = UIColor.redColor;
    // 设置边框宽度和颜色
    view1.layer.borderWidth = 2.0;
    view1.layer.borderColor = UIColor.blackColor.CGColor;
    // 设置圆角
    view1.layer.cornerRadius = 100.0;

    view1.center = self.view.center;
    [self.view addSubview:view1];
}

我们看到只有背景色、边框以及圆角的时候,确实不会触发离屏渲染。

  • 设置layer.masksToBounds或者clipsToBounds为YES
- (void)viewDidLoad {
    [super viewDidLoad];
    UIView *view1 = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 200.0, 200.0)];
    // 设置背景色
    view1.backgroundColor = UIColor.redColor;
    // 设置边框宽度和颜色
    view1.layer.borderWidth = 2.0;
    view1.layer.borderColor = UIColor.blackColor.CGColor;
    // 设置圆角
    view1.layer.cornerRadius = 100.0;

    // 设置裁剪
    view1.clipsToBounds = YES;

    view1.center = self.view.center;
    [self.view addSubview:view1];
}

当我们开启layer.masksToBounds或者clipsToBounds时,同样的没有触发离屏渲染。这是因为我们还没有设置图片。

  • 设置layer.masksToBounds或者clipsToBounds为YES,同时设置图片
- (void)viewDidLoad {
    [super viewDidLoad];
    UIView *view1 = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 200.0, 200.0)];
    // 设置背景色
    view1.backgroundColor = UIColor.redColor;
    // 设置边框宽度和颜色
    view1.layer.borderWidth = 2.0;
    view1.layer.borderColor = UIColor.blackColor.CGColor;

    //设置图片
    view1.layer.contents = (__bridge id)[UIImage imageNamed:@"pkq"].CGImage;

    // 设置圆角
    view1.layer.cornerRadius = 100.0;
    // 设置裁剪
    view1.clipsToBounds = YES;
    view1.center = self.view.center;
    [self.view addSubview:view1];
}

当我们开启layer.masksToBounds或者clipsToBounds时,同时设置图片时,就会触发离屏渲染。

  • 其实不光是图片,我们为视图添加一个有颜色、内容或边框等有图像信息的子视图也会触发离屏渲染。

有图像信息还包括在视图或者layer的draw方法中进行绘制等。

- (void)viewDidLoad {
    [super viewDidLoad];
    UIView *view1 = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 200.0, 200.0)];
    // 设置背景色
    view1.backgroundColor = UIColor.redColor;
    // 设置边框宽度和颜色
    view1.layer.borderWidth = 2.0;
    view1.layer.borderColor = UIColor.blackColor.CGColor;
    // 设置圆角
    view1.layer.cornerRadius = 100.0;
    // 设置裁剪
    view1.clipsToBounds = YES;

    // 子视图
    UIView *view2 = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100.0, 100.0)];
    // 下面3个任何一个属性
    // 设置背景色
    view2.backgroundColor = UIColor.blueColor;
    // 设置内容
    view2.layer.contents = (__bridge id)([UIImage imageNamed:@"pkq"].CGImage);
    // 设置边框
    view2.layer.borderWidth = 2.0;
    view2.layer.borderColor = UIColor.blackColor.CGColor;
    [view1 addSubview:view2];

    view1.center = self.view.center;
    [self.view addSubview:view1];
}

  1. 圆角触发离屏渲染的真正原因

图层的叠加绘制大概遵循“画家算法”。

油画算法:先绘制场景中的离观察者较远的物体,再绘制较近的物体。

先绘制红色部分,再绘制⻩色部分,最后再绘制灰⾊部分,即可解决隐藏面消除的问题。即将场景按照物理距离和观察者的距离远近排序,由远及近的绘制即可。

当我们设置了cornerRadius以及masksToBounds进行圆角+裁剪时,masksToBounds裁剪属性会应用到所有的图层上。

本来我们从后往前绘制,绘制完一个图层就可以丢弃了。但现在需要依次在 Offscreen Buffer中保存,等待圆角+裁剪处理,即引发了 离屏渲染

  • 背景色、边框、背景色+边框,再加上圆角+裁剪,根据文档说明,因为 contents = nil 没有需要裁剪处理的内容,所以masksToBounds设置为YES或者NO都没有影响。
  • 一旦我们 为contents设置了内容 ,无论是图片、绘制内容、有图像信息的子视图等,再加上圆角+裁剪,就会触发离屏渲染。

不一定是直接为contents赋值!

  1. iOS9及以后的优化

关于圆角,iOS 9及之后的系统版本,苹果进行了一些优化。

  • layer.contents/imageView.image

我们只设置contents或者UIImageView的image,并加上圆角+裁剪,是不会产生离屏渲染的。但如果加上了背景色、边框或其他有图像内容的图层,还是会产生离屏渲染。

- (void)viewDidLoad {
    [super viewDidLoad];
    UIView *view1 = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 200.0, 200.0)];
    //设置图片
    view1.layer.contents = (__bridge id)[UIImage imageNamed:@"qiyu"].CGImage;
    // 设置圆角
    view1.layer.cornerRadius = 100.0;
    // 设置裁剪
    view1.clipsToBounds = YES;

    view1.center = self.view.center;
    [self.view addSubview:view1];
}

其实这也是可以理解的,因为只有 单层 内容需要添加圆角和裁切,所以可以不需要用到离屏渲染技术。但如果加上了背景色、边框或其他有图像内容的图层,就会产生为 多层 添加圆角和裁切,所以还是会触发离屏渲染(如1中的第3个例子)。

所以,我们在使用类似于UIButton的视图的时候需要注意:

  • UIButton
- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor whiteColor];
    // 创建一个button视图
    UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 200.0, 200.0)];
    //设置图片
    [button setImage:[UIImage imageNamed:@"pkq"] forState:UIControlStateNormal];
    button.center = self.view.center;
    [self.view addSubview:button];
}

我们为UIButton设置一个图片,其实会添加一个UIImageView。

  • 为UIButton添加圆角和裁剪,则会触发离屏渲染。
// 设置圆角
button.layer.cornerRadius = 100.0;
// 设置裁剪
button.clipsToBounds = YES;

  • 如果改为UIButton中的UIImageView添加圆角和裁剪,则 不会触发离屏渲染。
// 设置圆角
button.imageView.layer.cornerRadius = 100.0;
// 设置裁剪
button.imageView.clipsToBounds = YES;


https://mp.weixin.qq.com/s/RFIiwvEhdSBub7HFm3C1Ug

闲鱼如何解决iOS环境搭建与APP打包速度问题

随着Flutter等跨端框架的出现,业务开发同学经常需要在Android/IOS上跨端进行业务开发,问题定位等。新的不熟悉的环境的搭建总会遇到各种各样的问题,导致搭建失败,特别是IOS开发环境,是最复杂的,不仅环境搭建繁琐,而且切分支后的打包速度很慢,所以我们设计实现了两个工具,用于优化闲鱼IOS开发体验。

发布于:17天以前  |  114次阅读  |  详细内容 »

iOS14 隐私适配及部分解决方案

在刚刚结束的线上 WWDC 2020 发布会上苹果向我们展示了新的 iOS14 系统。iOS14 的适配,很重要的一环就集中在用户隐私和安全方面。 在 iOS13 及以前,当用户首次访问应用程序时,会被要求开放大量权限,比如相册、定位、联系人,实际上该应用可能仅仅需要一个选择图片功能,却被要求开放整个照片库的权限,这确实是不合理的。对于相册,在 iOS14 中引入了 “LimitedPhotos Library” 的概念,用户可以授予应用访问其一部分的照片,对于应用来说,仅能读取到用户选择让应用来读取的照片,让我们看到了 Apple 对于用户隐私的尊重。这仅仅是一部分,在iOS14 中,可以看到诸多类似的保护用户隐私的措施,也需要我们升级适配。 最近在调研 iOS14的适配方案,本文主要分享一下 iOS14 上对于隐私授权的变更和部分适配方案,欢迎补充指正。

发布于:17天以前  |  97次阅读  |  详细内容 »

iOS 隐形水印之 LSB 实现

在音视频的领域里,其涵盖的知识点繁多,学习方向也很多。而本篇就是一篇比较入门的文章它简单地介绍如何在 iOS 上读取图片 RGB 数据,并通过修改最后一位 bit 来记录数字水印的信息下面就介绍《隐形水印之 iOS 实现》

发布于:1月以前  |  239次阅读  |  详细内容 »

声明式 UIKit 在有赞美业的实践

随着 Flutter 的出现,UI 开发形式也越来越趋向相同,Flutter,SwiftUI,RN,Weex 等新兴UI框架无一意外都使用了声明式的 UI 开发模式,和支持了FlexBox的布局系统。

发布于:1月以前  |  241次阅读  |  详细内容 »

iOS 架构谈:剖析 Uber 的 RIB 架构

加入 UBER 是我的 iOS 工程师职业的新篇章,所有这一切都始于称为 RIB 的新架构。该架构背后的主要思想是,应用程序应由业务逻辑而不是视图驱动。展示 RIB 的最佳方法是一棵树:每个 RIB 都是一个节点,并且它可以不包含子节点,也可以包括一个或多个子节点。

发布于:1月以前  |  215次阅读  |  详细内容 »

如何调试支付宝(iOS)

最近在做的一件事情,从代码层面分析下各家小程序(微信、头条、支付宝、百度)的启动性能,探究各家小程序的实现细节和差异。

发布于:2月以前  |  236次阅读  |  详细内容 »

iOS GPUImage源码解读(一)

最近在不断学习、使用的过程中,有了更深刻的理解,特来写一篇源码解读的文章详细介绍下核心代码的具体实现。至于括号里的“一”,主要是觉得GPUImage还有很多值得深入学习和分享的内容,后续的学习和使用过程中有新的心得体会还会继续给大家分享。

发布于:2月以前  |  253次阅读  |  详细内容 »

iOS开发之Masonry框架源码解析

Masonry是iOS在控件布局中经常使用的一个轻量级框架,Masonry让NSLayoutConstraint使用起来更为简洁。Masonry简化了NSLayoutConstraint的使用方式,让我们可以以链式的方式为我们的控件指定约束。本篇博客的主题不是教你如何去使用Masonry框架的,而是对Masonry框架的源码进行解析,让你明白Masonry是如何对NSLayoutConstraint进行封装的,以及Masonry框架中的各个部分所扮演的角色是什么样的。在Masonry框架中,仔细的品味干货还是很多的。Masonry框架是Objective-C版本的,如果你的项目是Swift语言的,那么就得使用SnapKit布局框架了。SnapKit其实就是Masonry的Swift版本,两者虽然实现语言不同,但是实现思路大体一致。

发布于:2月以前  |  236次阅读  |  详细内容 »

iOS 验证码输入一种实现思路

如图所示,现在很多App采用了类似下划线、方块等方式的验证码输入,直观美观!对于这种效果的实现方式,大概有以下几种方式:

发布于:2月以前  |  224次阅读  |  详细内容 »

最多阅读

快速配置 Sign In with Apple 1年以前  |  2690次阅读
给数组NSMutableArray排序 1年以前  |  2166次阅读
开篇 关于iOS越狱开发 1年以前  |  2087次阅读
在越狱的iPhone设置上使用lldb调试 1年以前  |  2044次阅读
UITableViewCell高亮效果实现 1年以前  |  2016次阅读
APP适配iOS11 1年以前  |  1988次阅读
App Store 审核指南[2017年最新版本] 1年以前  |  1767次阅读
所有iPhone设备尺寸汇总 1年以前  |  1738次阅读
关于Xcode不能打印崩溃日志 1年以前  |  1738次阅读
使用 GPUImage 实现一个简单相机 1年以前  |  1734次阅读
使用ssh访问越狱iPhone的两种方式 1年以前  |  1688次阅读
使用ssh 访问越狱iPhone的两种方式 1年以前  |  1608次阅读
UIDevice的简单使用 1年以前  |  1513次阅读
为对象添加一个释放时触发的block 1年以前  |  1430次阅读
使用最高权限操作iPhone手机 1年以前  |  1398次阅读