美文网首页IOS高性能组件
看山不是山,再谈iOS圆角优化

看山不是山,再谈iOS圆角优化

作者: AKTeamYang | 来源:发表于2019-10-21 19:32 被阅读0次

    设计趋势

    随着移动互联网的发展,在UI设计领域,圆角和阴影逐渐成为一种不可或缺的设计手法,这离不开系统厂商对于设计风格的引导,比如:Apple产品的设计风格和UI设计规范、Google Material Design等等。不可否认,圆角设计确实会增强产品的亲和力和优雅感,但是作为开发者,我们却不那么喜欢它,因为通常大量圆角+阴影会显著降低界面滑动的流畅度。

    image.png

    是什么造成了卡顿

    有没有圆角和阴影对于CPU来说没有太大差别,额外的效果会给GPU带来较大的压力。通过Xcode提供的,View debugging可以看到,有没有圆角在渲染上的差别在于离屏渲染。离屏渲染就是图层无法直接在当前屏幕绘制,而要开辟新的缓冲区进行绘制,然后在切换到当前缓冲区进行展示,中间就包含了不少性能消耗。

    离屏渲染发生与否取决于技术实现,iOS在发展过程中,也在尽量的减少离屏渲染发的发生。例如:在iOS9上设置了圆角并且maskToBounds就会触发离屏渲染,而在iOS11以后则需要在子视图也设置了圆角时才会触发。所以最好的办法就是通过Xcode提供的View debugging来查看。

    优化之预合成圆角

    在圆角优化方面,目前主流的技术方案是,图片下载下来后根据圆角大小来重新绘制一张切过圆角的PNG图片,然后将此图片展示到UI控件上,这样控件就不必再设置圆角。

    image.png

    预合成圆角避免了离屏渲染,性能表现很不错,但是依然存在几点问题:

    1. 如果控件是Gif或者视频,预先渲染将无法实施
    2. 每张图片都需要重新绘制,会带来额外的CPU开销
    3. 原始图片和重绘后的图片都需要存入缓存,带来较大的内存占用
    4. 同样一张图片,如果展示的尺寸和圆角值不一样,则需要重绘多个版本
    5. 通常图标上会加入子视图,无法被天然裁切
    image.png

    换个思路,探索适用于90%场景的障眼之法

    虽然我们看到的是圆角,但可能实际不是个圆角。相信有人用过这种方式生成圆角效果,简单高效,通过图层叠加,让图片看起来像是有圆角。这种方案的问题在于:不同尺寸、圆角大小、描边及阴影效果都需要提供单独的图片,通用性非常不好。

    image.png

    抛开问题不说,这条技术路线有没有价值呢?为此统计了大多数app中的圆角设计,发现绝大多数圆角阴影效果都可以通过这种方式实现。

    image.png

    不能实现的是背景非纯色的场景

    image.png

    所以总体看来基于图层叠加的圆角优化技术有其独特价值。

    圆角与阴影的实现

    利用CoreGraphics可以绘制图片的圆角、阴影和描边,具体的不再展开。

    image.png
    image.png

    尺寸的自适应

    关键问题来了,这个蒙层要绘制多大?

    通常一套页面设计中,控件都使用一致的圆角和阴影效果,但是控件尺寸会有不同。如果每个尺寸的控件都要绘制一套蒙层,虽然技术上可以实现,但是不免让人遗憾。

    经过反复求证,发现求得一个最小尺寸,来满足更大尺寸的自适应是可行的,这就解决了一张图适用多个尺寸的问题。

    image.png

    受限于cornerRadius、offset、blur的大小,自适应蒙层有一个自适应最小值,当控件尺寸小于蒙层尺寸时则需通过另一套策略绘制,在工程实践中还没有遇到这种场景,这一块暂时没有支持。

    CALayer中关于自适应的设置,contentCenter的范围是0~1.0

    @property CGRect contentsCenter
    

    SKCornerLayer支持的效果

    圆角 阴影 描边 异步 缓存 DarkMode

    使用方法

    // 设置阴影的颜色、blur、offset、圆角支持dark mode
    self.shadowView.layer.skCorner
            .sShadowColor(colorDynamicFromRGBA(0x535459, 0.15, 0x0, 0))
            .sShadowBlur(15)
            .sShadowOffset(CGSizeMake(0, 2))
            .sCornerRadiusAll(8)
            .sCornerBackgroundColor(colorDynamicFromRGB(0xffffff, 0x1e1e1e));
    
    // 异步绘制
    self.shadowView.layer.skCornerLayer.displayCornerAsynchronously = YES;
    

    性能量化

    这里构了一个场景,左边使用SKCornerLayer,右边使用默认的圆角+阴影(采用了默认最优配置:圆角view没有嵌套,阴影指定shadowPath,没有产生离屏渲染)。测试中采用,通过timer以相同的速度滚动列表,以产生稳定的GPU占用率。

    image.png image.png

    在iOS9上即使圆角后的view没有嵌套,也会产生离屏渲染,GPU开销会更大

    测试设备iPhone 6sPlus iOS12.2

    这里设置了两个对比组,blur分别为15和5,分别可以提升GPU性能约40%和28%,总体来看blur值越大优化效果越好。

    image.png

    关于圆角和阴影的一些建议

    1. 父视图有圆角时避免子视图包含圆角效果,解决办法是,视图层级扁平化
    2. 对性能要求不高的场景下,通过指定shadowPath来生成阴影也是个不错的选择
    3. 对性能有较高要求的场景下,建议使用SKCornerLayer或者切图来构建阴影
    4. 最后使用View debugging检查视图

    相关文章

      网友评论

        本文标题:看山不是山,再谈iOS圆角优化

        本文链接:https://www.haomeiwen.com/subject/zblxvctx.html