ZaatarLABS
联系我们 →
← 全部文章
ZaatarLABS · 工程

实测Liquid Glass

两个数字——每分钟120次卡顿和5次——说清了Liquid Glass的真实代价,以及这份代价落在哪里。

阅读约4分钟

让我停下来的数字是120。每分钟卡顿次数,Instruments开着,屏幕上十张玻璃卡片,光泽动画正在运行。我一直以为倾斜响应基本是零成本的——它看起来很对,像真玻璃一样追随光线,感觉就是那个恰到好处的细节。然后我测了一下。

在十五张卡片上改用静态光泽后,这个数字降到了五。卡片更多,没有动效:列表更满,每分钟卡顿却少了115次。玻璃留下了,动画没有。

这个落差,就是这篇文章的重点。

我先排除了什么

最显而易见的假设是卡片数量。屏幕上的表面越多,工作量越大——这个逻辑在很多渲染问题上都成立,我追着它查的时间比应该花的要长。我实际尝试过的:从60 Hz降到30 Hz。把光泽从SwiftUI渲染改到CALayer。动效门控,也就是只有在加速度计显示设备静止时才运行动画。限制最高那张卡片的高度,让它无法超过一个固定值。这些都没能让数字产生有意义的变化。

真正奏效的修复也是最简单的:motionEnabled = false。静态光泽,没有倾斜响应,没有逐帧采样。

系统实际上在做什么

.glassEffect的工作方式是对它的背景进行采样。正是这一点让它看起来像玻璃,而不是一块磨砂矩形——它读取身后的内容,在采样结果上施加模糊、着色和高光。这个采样是实时的。当玻璃表面上方有任何东西在动时,系统必须为屏幕上的每一个玻璃表面重新合成背景,而不只是离动效最近的那一个。

十张卡片。每秒六十帧。上方运行着光泽动画。每个表面每秒六十次重新合成,再乘以视图层级中的每一张玻璃卡片。每分钟120次卡顿。

十五张卡片,静态光泽:背景只采样一次,然后保持不变,直到有东西发生变化。每分钟五次卡顿。

已发布的版本中,高光边缘依然存在。它从一个固定角度打光,永远不动。必须去掉的是倾斜响应——那个追踪设备朝向、让高光随着您移动手机而移动的部分。卡片看起来仍然是玻璃,只是不再对重力做出反应。

面积才是真正的成本驱动因素

一旦弄清了重新合成的模型,另一件事也随之明朗:成本随背景面积增长,而不是随玻璃表面的数量增长。

三十五张窄卡片的成本低于十张高卡片。系统合成的是屏幕上被玻璃覆盖的总面积,一张小卡片贡献的成本按比例更少,不管有多少张。列表顶部的一张统计卡片——小巧、高度固定——很便宜。一张几乎横跨整个屏幕宽度、还会随内容增高的卡片则很昂贵。

这改变了设计问题。问题不再是“这个屏幕上我能负担得起多少个玻璃表面?”,而是“在任意滚动位置,视口中有多大比例是玻璃?”

高卡片的问题

有些卡片必须很高。比如预约卡片,随着条目增加,它可以无限增高。如果不加约束,它的背景面积会随之增长,每帧的成本也一样。

解决办法是限制高度。给卡片设定一个最大高度,让内容在内部滚动。无论里面有多少行,玻璃系统看到的这个表面都是一个瓦片大小的常量。卡片之所以负担得起,是因为它的背景面积有了上限。

这是那种即使不考虑性能也能改善设计的约束。一张无限增高的卡片,反正也往往会把其他所有东西都挤出屏幕。

两个值得了解的实现细节

GlassEffectContainer会把同级的玻璃表面合并到一次合成中。如果您在层级中同一层有好几个玻璃元素——一排统计卡片、一个网格——用这个容器把它们包起来是值得的。不包的话,每个表面都会独立合成。

另一个细节:在修饰符链中,.clipShape必须放在.background之前。

// Wrong — the background bleeds past the corners
.background(material)
.clipShape(RoundedRectangle(cornerRadius: 16))

// Right
.clipShape(RoundedRectangle(cornerRadius: 16))
.background(material)

玻璃材质不会裁剪它的子视图。如果裁剪放在后面,一个自己绘制背景的行就会在卡片的圆角形状之外画出直角。我是在一个带内部行的列表上吃了苦头才发现这一点的。

这在设计上付出了什么

倾斜响应没了。它是最明显的玻璃特性——高光随着您移动手机而移动——也是当性能分析工具显示120时您要放弃的东西。

留下来的:磨砂的主体、高光边缘、用于光泽高光的角度渐变。卡片看起来是玻璃,但在物理意义上,它的行为并不像玻璃。

测量结果还带来了另一条约束:统计卡片保持透明玻璃,绝不着色。在白色标签的卡片上效果很好的着色,会让主色调的文字完全看不见。颜色编码应该放在数据里——迷你走势图、指示器——而不是放在表面上。

文件本身就是文档

LiquidGlassCard.swift在The Smart Dentist、Billing和Coach中被原样复制。这是有意为之。性能规则跟着文件走,而不是记在某处的笔记里。从120到5的来龙去脉和代码写在同一个源文件里,紧挨着各个调节参数和注释,这样下一个打开它的人就不必把这些重新摸索一遍。

数字就是设计文档。其余的都是注释。

作者:Omar Al Homaidi · ZaatarLABS · @zaatarlabs