iOS 26 Liquid Glass:层次感的克制与组织
iOS 26 带来了 Liquid Glass——一种会模糊身后内容、从周围环境反射颜色和光线、还能对触摸做出形变反应的动态材质。苹果这次把它做进了 SwiftUI、UIKit 和 WidgetKit,API 设计相当克制,但「克制」本身就是一个需要花时间理解的原则。
我踩过的第一个弯路是:看到 .glassEffect() 这么好用,一不小心往每个卡片、每个 label 上都加了一遍。结果页面变成一锅毛玻璃粥——没有主次,反而失去了深度感。Liquid Glass 的价值在于选择性,把它留给真正需要浮于内容之上的元素。
三件事决定玻璃效果是否到位
第一件事:Container 不能省。 当页面里有两个以上相邻的 glass view 时,务必把它们套进 GlassEffectContainer:
GlassEffectContainer(spacing: 40.0) {
HStack(spacing: 40.0) {
Image(systemName: "scribble.variable")
.frame(width: 80, height: 80)
.glassEffect()
Image(systemName: "eraser.fill")
.frame(width: 80, height: 80)
.glassEffect()
}
}spacing 参数控制「融合距离」——两个 glass 元素靠得足够近时,它们的轮廓会自然地流体合并。不用 Container 就失去了这个行为,每个元素各自独立渲染,性能也更差。
第二件事:morphing 需要 namespace 配合。 玻璃元素出现或消失时,想要平滑的形变动画,需要给每个元素声明身份:
@Namespace private var glassSpace
Image(systemName: "pencil")
.glassEffect()
.glassEffectID("pencil", in: glassSpace)再配合 withAnimation { isExpanded.toggle() } 触发状态切换,系统就知道该对哪两个形状做插值过渡。少了 glassEffectID,出现消失只是普通的 opacity 动画,完全没有 Liquid Glass 的流体质感。
第三件事:.interactive() 要显式开启。 不是每块玻璃都应该对触摸作出反应——静态的背景卡片不需要,工具栏按钮才需要。interactive() 是明确的 opt-in:
Button("操作") { }
.buttonStyle(.glass) // 内置样式已包含 interactive
// 或者手动:
.glassEffect(.regular.tint(.orange).interactive(), in: .rect(cornerRadius: 16))Liquid Glass 不是装饰手段,是「层次语言」。只给真正浮在内容上层的元素加玻璃效果:导航栏、工具栏、浮动按钮、卡片容器。背景、文字、图表——不加。
Widget 的特殊处理
在 WidgetKit 里,还需要应对一个额外维度:用户可能开启了「着色主屏」,系统会把 widget 渲染成单色玻璃模式。忘记处理这个分支,就会在某些主屏主题下出现视觉错乱:
@Environment(\.widgetRenderingMode) var renderingMode
var body: some View {
if renderingMode == .accented {
// 着色模式:只保留结构,颜色让系统接管
} else {
// 正常全彩模式
}
}对需要在着色模式下保持清晰的图片,加 .widgetAccentedRenderingMode(.monochrome) 让系统统一处理色调。
反模式速查
- 不用 Container 直接堆多个 glass:失去形变合并,性能下降
- 对每个视图都加 glass:视觉噪声,层次感消失
- UIKit 里忘了
clipsToBounds = true:圆角不生效,玻璃溢出父视图 - 背景用不透明色:遮住了模糊层,glass 效果等于零
- Widget 不处理 accented 模式:着色主屏下外观破坏
一句话心法
玻璃是层次的语言,不是装饰的手段——每多加一块,就多问一次「它真的浮在内容上方吗?」