说实话,第一次在 iOS 26 上看到 Liquid Glass 的效果时,我愣了好几秒。那种半透明、有折射、会跟着手指动的玻璃质感,真的和之前的毛玻璃完全不是一回事。如果你最近升级了 Xcode 26 或者看了 WWDC 2025 的回放,应该也有同样的感受——这是自 iOS 7 扁平化以来最大的一次视觉变革。
好消息是,Apple 给了我们一整套 SwiftUI API 来实现这个效果。坏消息?中文社区能找到的系统性教程少得可怜,基本都是零散的代码片段或者英文文档的机翻。
所以这篇文章来了。
我会从最基础的 .glassEffect() 修饰符讲起,一路覆盖到 GlassEffectContainer、变形动画、自定义组件封装,还有向后兼容和无障碍适配。每一步都附带可以直接跑的代码,看完之后你应该能在自己的项目里自信地用上 Liquid Glass。
Liquid Glass 到底是什么?
先澄清一点:Liquid Glass 不是一个简单的毛玻璃。它是一种动态材质,具备这些特性:
- 实时折射和反射——玻璃下方的内容会被折射,表面会反射周围环境
- 响应设备运动——倾斜 iPhone 时,玻璃上的高光会跟着动(这个细节太赞了)
- 自适应阴影——根据背景内容自动调整阴影深度
- 触摸交互反馈——按下时玻璃会有物理性的凹陷感
Apple 的设计意图很明确:Liquid Glass 是给导航层用的。工具栏、Tab Bar、浮动按钮——这些悬浮在内容之上的控件最适合。而列表、卡片、媒体这些主要内容区域就别用了,不然整个界面会变成一坨模糊。
记住这条原则,能帮你避开很多坑。
环境要求
动手写代码之前,先确认一下你的开发环境:
- Xcode 26 或更高版本
- 部署目标设为 iOS 26.0+
- Swift 6 或更高版本
Liquid Glass 的 API 是 iOS 26 SDK 独有的。如果你的项目还得兼容旧版本,别急,后面专门有一节讲向后兼容。
glassEffect() 修饰符:一切的起点
.glassEffect() 是使用 Liquid Glass 最直接的方式。一行代码就够了:
import SwiftUI
struct BasicGlassDemo: View {
var body: some View {
Text("Hello, Liquid Glass!")
.font(.headline)
.padding()
.glassEffect()
}
}
就这么简单。默认情况下它会用 .regular 样式,形状是胶囊形。完整的方法签名长这样:
func glassEffect<S: Shape>(
_ glass: Glass = .regular,
in shape: S = DefaultGlassEffectShape,
isEnabled: Bool = true
) -> some View
三个参数都是可选的,灵活组合就行。
Glass 样式类型
Glass 支持三种基础样式:
.regular— 标准的半透明玻璃效果,绝大多数场景用这个.clear— 透明玻璃,保留光线折射但减少模糊.identity— 无玻璃效果,主要用于无障碍降级
自定义形状
玻璃效果不一定非得是胶囊形,任何符合 Shape 协议的类型都能用:
struct GlassShapesDemo: View {
var body: some View {
VStack(spacing: 20) {
// 胶囊形(默认)
Text("胶囊")
.padding()
.glassEffect(.regular, in: .capsule)
// 圆形
Image(systemName: "star.fill")
.font(.title)
.frame(width: 60, height: 60)
.glassEffect(.regular, in: .circle)
// 圆角矩形
Text("圆角矩形")
.padding(.horizontal, 24)
.padding(.vertical, 12)
.glassEffect(.regular, in: .rect(cornerRadius: 12))
// 自适应父容器圆角
Text("容器适配")
.padding()
.glassEffect(
.regular,
in: .rect(cornerRadius: .containerConcentric)
)
}
}
}
这里特别提一下 .containerConcentric——它会自动算出跟父容器同心的圆角半径,嵌套的玻璃元素看起来会特别和谐。个人觉得这是个被低估的小功能。
色调(Tint)
通过 .tint() 可以给玻璃染色:
struct TintedGlassDemo: View {
var body: some View {
HStack(spacing: 16) {
Text("蓝色")
.padding()
.glassEffect(.regular.tint(.blue))
Text("紫色")
.padding()
.glassEffect(.regular.tint(.purple.opacity(0.6)))
Text("橙色")
.padding()
.glassEffect(.regular.tint(.orange))
}
}
}
色调不会把透明感盖掉,而是像给玻璃染了一层很薄的颜色。实际效果比你想象的要优雅。
交互效果(Interactive)
加上 .interactive(),玻璃在用户触摸时就会有物理反馈——按下去轻微凹陷,松开弹回来:
Button("点击我") {
// 处理点击
}
.padding()
.glassEffect(.regular.interactive())
这个效果做自定义按钮的时候特别好用。当然 Apple 也给了现成的玻璃按钮样式,懒得自己写的话直接用就好:
Button("玻璃按钮") { }
.buttonStyle(.glass)
Button("强调玻璃按钮") { }
.buttonStyle(.glassProminent)
.glass 是标准玻璃样式,.glassProminent 会把按钮的 tint 颜色铺到整个玻璃表面,视觉上更抢眼。
GlassEffectContainer:多个玻璃的协调容器
页面上只有一个玻璃元素的时候一切都很美好。但当你有好几个玻璃元素挤在一起时,问题就来了。
核心原因:玻璃没法对另一个玻璃进行采样。两个玻璃元素靠太近,视觉上就会互相干扰,看着特别别扭。GlassEffectContainer 的解决方案很聪明——它会把距离够近的玻璃元素自动合并成一个统一的玻璃形状。
struct GlassContainerDemo: View {
var body: some View {
GlassEffectContainer {
HStack(spacing: 12) {
Button("首页") { }
.glassEffect()
Button("搜索") { }
.glassEffect()
Button("设置") { }
.glassEffect()
}
.padding()
}
}
}
三个按钮会自动融合成一个连续的玻璃形状。说真的,那个融合动画有点像水滴靠近时合并的感觉,很漂亮。
spacing 参数:控制融合阈值
GlassEffectContainer 的 spacing 参数决定了多近才触发融合:
// 间距小于 40pt 的元素会融合
GlassEffectContainer(spacing: 40) {
VStack(spacing: 30) {
Button("按钮 A") { }.glassEffect()
Button("按钮 B") { }.glassEffect()
// 这两个按钮间距 30 < 40,会融合
}
}
// 间距小于 10pt 才融合(更紧凑)
GlassEffectContainer(spacing: 10) {
HStack(spacing: 20) {
Button("按钮 A") { }.glassEffect()
Button("按钮 B") { }.glassEffect()
// 间距 20 > 10,不会融合,各自独立
}
}
设计复杂工具栏的时候,调这个值可以精确控制哪些元素要分组在一起。
glassEffectID:流畅的变形动画
接下来讲 Liquid Glass 里我个人最喜欢的部分——变形动画(Morph Animation)。
想象一下:一个 + 按钮,点击后展开成一排操作选项,整个过程中玻璃形状像液体一样流畅地变形。听起来很复杂对吧?但实现起来真没几行代码:
struct MorphingFABDemo: View {
@State private var isExpanded = false
@Namespace private var glassNS
var body: some View {
GlassEffectContainer(spacing: 18) {
VStack(spacing: 12) {
// 主按钮
Button {
withAnimation(.bouncy(duration: 0.35)) {
isExpanded.toggle()
}
} label: {
Image(systemName: isExpanded ? "xmark" : "plus")
.font(.title2)
.frame(width: 56, height: 56)
}
.glassEffect(.regular.interactive())
.glassEffectID("toggle", in: glassNS)
// 展开的操作按钮
if isExpanded {
Button {
// 拍照
} label: {
Image(systemName: "camera")
.frame(width: 48, height: 48)
}
.glassEffect()
.glassEffectID("camera", in: glassNS)
Button {
// 相册
} label: {
Image(systemName: "photo")
.frame(width: 48, height: 48)
}
.glassEffect()
.glassEffectID("photo", in: glassNS)
Button {
// 文件
} label: {
Image(systemName: "doc")
.frame(width: 48, height: 48)
}
.glassEffect()
.glassEffectID("doc", in: glassNS)
}
}
}
}
}
几个关键点说一下:
@Namespace提供命名空间,让 SwiftUI 能追踪哪些元素是"同一个".glassEffectID("标识符", in: namespace)给每个玻璃元素一个唯一身份- 搭配
withAnimation,元素出现和消失时会自动产生流畅的变形效果 .bouncy动画曲线跟 Liquid Glass 的物理质感简直绝配
glassEffectTransition:控制出场方式
默认情况下,玻璃元素用 materialize(实体化)的方式出现和消失。你也可以通过 .glassEffectTransition 来自定义:
Button("操作") { }
.glassEffect()
.glassEffectID("action", in: glassNS)
.glassEffectTransition(.materialize)
这个效果看起来就像元素从虚无中凝结成形,然后又化回虚无。非常"液态"。
glassEffectUnion:强制合并玻璃形状
有时候你需要多个元素共享同一块玻璃背景。最典型的例子就是 Apple 地图里的缩放控件——加号和减号按钮被一个统一的玻璃形状包裹着。
这时候就该 .glassEffectUnion 上场了:
struct ZoomControlDemo: View {
@Namespace private var zoomNS
var body: some View {
GlassEffectContainer {
VStack(spacing: 0) {
Button {
// 放大
} label: {
Image(systemName: "plus")
.frame(width: 44, height: 44)
}
.glassEffect(.regular)
.glassEffectUnion(id: "zoom", namespace: zoomNS)
Divider()
.frame(width: 30)
Button {
// 缩小
} label: {
Image(systemName: "minus")
.frame(width: 44, height: 44)
}
.glassEffect(.regular)
.glassEffectUnion(id: "zoom", namespace: zoomNS)
}
}
}
}
跟 glassEffectID 的区别在于:glassEffectUnion 让元素始终显示为一个整体,不管距离多远。而前者只在距离够近时才融合。
实战:自定义 Liquid Glass 浮动 Tab Bar
好了,理论够多了,来做个真实的东西。
下面是一个悬浮在内容上方的自定义 Tab Bar,带 Liquid Glass 效果和选中状态动画。我自己的项目里就在用类似的实现:
struct LiquidGlassTabBar: View {
@Binding var selectedTab: Int
@Namespace private var tabNS
let tabs: [(icon: String, label: String)] = [
("house", "首页"),
("magnifyingglass", "搜索"),
("heart", "收藏"),
("person", "我的")
]
var body: some View {
GlassEffectContainer(spacing: 8) {
HStack(spacing: 4) {
ForEach(Array(tabs.enumerated()), id: \.offset) { index, tab in
Button {
withAnimation(.smooth(duration: 0.3)) {
selectedTab = index
}
} label: {
VStack(spacing: 4) {
Image(systemName: selectedTab == index
? "\(tab.icon).fill" : tab.icon)
.font(.system(size: 20))
Text(tab.label)
.font(.caption2)
}
.frame(maxWidth: .infinity)
.padding(.vertical, 8)
}
.foregroundStyle(
selectedTab == index ? .primary : .secondary
)
.glassEffect(
selectedTab == index
? .regular.tint(.blue.opacity(0.3))
: .clear,
in: .capsule
)
.glassEffectID("tab-\(index)", in: tabNS)
}
}
.padding(.horizontal, 8)
.padding(.vertical, 4)
.glassEffect(.regular, in: .capsule)
}
}
}
// 使用方式
struct ContentView: View {
@State private var selectedTab = 0
var body: some View {
ZStack(alignment: .bottom) {
// 你的主要内容
TabContent(selectedTab: selectedTab)
LiquidGlassTabBar(selectedTab: $selectedTab)
.padding(.horizontal, 40)
.padding(.bottom, 20)
}
}
}
这个实现的巧妙之处在于:外层一个大胶囊形玻璃做整体背景,选中的 Tab 又有自己带色调的小玻璃高亮。切换 Tab 时,高亮通过 glassEffectID 流畅地滑动变形,体验非常丝滑。
实战:Liquid Glass 卡片组件
虽然 Apple 建议不要在内容层用 Liquid Glass,但有一种情况例外——悬浮在内容之上的信息卡片(播放器控件、快捷操作面板之类的),用玻璃效果反而很合适:
struct GlassInfoCard: View {
let title: String
let subtitle: String
let icon: String
var body: some View {
HStack(spacing: 16) {
Image(systemName: icon)
.font(.system(size: 28))
.foregroundStyle(.blue)
.frame(width: 48, height: 48)
.glassEffect(.regular.tint(.blue.opacity(0.2)), in: .circle)
VStack(alignment: .leading, spacing: 4) {
Text(title)
.font(.headline)
Text(subtitle)
.font(.subheadline)
.foregroundStyle(.secondary)
}
Spacer()
Image(systemName: "chevron.right")
.foregroundStyle(.tertiary)
}
.padding()
.glassEffect(.regular, in: .rect(cornerRadius: 16))
}
}
// 使用示例
struct CardListDemo: View {
var body: some View {
ZStack {
// 背景图片
Image("background")
.resizable()
.ignoresSafeArea()
VStack(spacing: 12) {
GlassInfoCard(
title: "正在播放",
subtitle: "周杰伦 - 晴天",
icon: "music.note"
)
GlassInfoCard(
title: "下一个日程",
subtitle: "14:00 团队会议",
icon: "calendar"
)
}
.padding()
}
}
}
向后兼容:支持 iOS 26 以下版本
现实情况是,大部分应用不可能只支持最新系统。如果你的应用还得兼容 iOS 26 之前的版本,glassEffect 相关 API 直接就编译不过。
解决方案是用 #available 做版本隔离。不过与其到处写判断,不如封装一个通用的 View Extension,一劳永逸:
extension View {
@ViewBuilder
func adaptiveGlass(
in shape: some Shape = .capsule
) -> some View {
if #available(iOS 26.0, *) {
self.glassEffect(.regular, in: shape)
} else {
self.background(.ultraThinMaterial, in: shape)
}
}
@ViewBuilder
func adaptiveGlassInteractive(
in shape: some Shape = .capsule
) -> some View {
if #available(iOS 26.0, *) {
self.glassEffect(.regular.interactive(), in: shape)
} else {
self
.background(.ultraThinMaterial, in: shape)
.shadow(color: .black.opacity(0.1), radius: 4)
}
}
}
这样业务代码里只要调 .adaptiveGlass() 就行了。iOS 26 自动走 Liquid Glass,旧版本优雅降级为 .ultraThinMaterial。干净利落。
无障碍(Accessibility)适配
Liquid Glass 效果好看归好看,但不是所有用户都适合。有些用户对透明效果敏感,iOS 提供了"减少透明度"选项,你的应用得尊重这个设置:
struct AccessibleGlassView: View {
@Environment(\.accessibilityReduceTransparency)
var reduceTransparency
var body: some View {
Text("自适应玻璃")
.padding()
.glassEffect(
reduceTransparency ? .identity : .regular,
in: .capsule
)
}
}
.identity 会完全移除玻璃效果,让视图回到原始外观。大多数时候系统会自动处理这件事,但如果你做了高度自定义的玻璃组件,手动检查一下这个环境变量还是有必要的。
性能优化建议
Liquid Glass 涉及实时渲染——模糊、折射、反射,这些都是有性能开销的。不注意的话帧率说掉就掉。
- 避免大面积使用——玻璃面积越大渲染越重,用在导航控件上就够了,别铺满屏幕
- 控制嵌套层数——玻璃对玻璃采样效果很差,多层嵌套既丑又费性能
- 善用 GlassEffectContainer——它不光让视觉更协调,合并渲染还能提升性能,一举两得
- 别在滚动列表里加玻璃——快速滚动 + 玻璃渲染 = 掉帧,亲测
- 用 Instruments 验证——Core Animation 工具能帮你定位玻璃渲染导致的性能瓶颈
常见设计反模式
用了几周 Liquid Glass 之后,我总结了一些常见的坑:
- 不要给列表的每一行加玻璃——视觉过载,整个列表变成一片模糊,用户根本看不清内容
- 不要叠加多层玻璃——会产生奇怪的视觉伪影,特别难看
- 不要在纯色背景上用——Liquid Glass 的魅力在于折射下面丰富的内容,纯色背景上它就是个灰色块,完全没有质感
- 注意 Tab Bar 的可读性——内容在下面滚动时,彩色内容可能会让玻璃上的文字变得模糊不清
Tab Bar 可读性修复
上面提到的 Tab Bar 可读性问题其实很好解决。加一个底部渐变遮罩就行:
struct TabBarFadeModifier: ViewModifier {
func body(content: Content) -> some View {
content
.overlay(alignment: .bottom) {
LinearGradient(
colors: [
.clear,
Color(.systemBackground).opacity(0.8)
],
startPoint: .top,
endPoint: .bottom
)
.frame(height: 100)
.allowsHitTesting(false)
}
}
}
extension View {
func tabBarFade() -> some View {
modifier(TabBarFadeModifier())
}
}
// 在你的可滚动内容上使用
ScrollView {
// 你的内容
}
.tabBarFade()
简单粗暴但有效。
与 UIKit 混合使用
如果你的项目是 UIKit 和 SwiftUI 混合架构(说实话,大部分生产项目都是),UIKit 也有对应的 Liquid Glass API:
// UIKit 中创建玻璃效果
let container = UIGlassContainerEffect()
let containerView = UIVisualEffectView(effect: container)
let glassEffect = UIGlassEffect()
let glassView = UIVisualEffectView(effect: glassEffect)
containerView.contentView.addSubview(glassView)
// 启用交互反馈
glassEffect.isInteractive = true
// 移除时使用 effect = nil(不要用 alpha)
UIView.animate(withDuration: 0.3) {
glassView.effect = nil
}
这里有个容易踩的坑:移除玻璃效果时,一定要通过 effect = nil 来触发 dematerialization 动画,千万别改 alpha。改 alpha 不会有那个优雅的消失效果,而且可能导致视觉 bug。
常见问题解答
Liquid Glass 会在现有应用上自动生效吗?
部分会。用 Xcode 26 重新编译并以 iOS 26 为目标之后,系统内置组件(NavigationBar、TabBar、Toolbar 等)会自动获得 Liquid Glass 外观,不用改一行代码。但你自定义的控件不会自动变,得手动加 .glassEffect()。
glassEffect 和 .ultraThinMaterial 有什么区别?
.ultraThinMaterial 是 iOS 15 引入的静态毛玻璃——只有模糊和半透明,仅此而已。.glassEffect() 则是全新的动态材质,折射、反射、设备运动响应、触摸交互反馈全都有。两者的视觉层次感完全不在一个级别。当然了,对于还得兼容旧版本的项目,.ultraThinMaterial 仍然是最好的降级方案。
watchOS 和 tvOS 上能用吗?
可以。Apple 在 WWDC 2025 宣布 Liquid Glass 会覆盖所有平台,包括 watchOS 26 和 tvOS 26。不过具体 API 的可用性各平台可能有差异,建议查一下对应平台的 SDK 文档。
用 Liquid Glass 会影响 App Store 审核吗?
不会。这是 Apple 自家的设计语言和 SDK 组件,正常用完全没问题。事实上,积极适配新设计语言的应用更容易获得 App Store 的编辑推荐。
怎么在 SwiftUI Preview 中调试?
Liquid Glass 在 Preview 中可以正常渲染,但由于 Preview 没有设备运动数据,倾斜高光之类的动态效果看不到。最终的视觉验证还是得上真机(或者至少用模拟器)。另外有个小技巧:Preview 的背景别用纯色,换成一张图片,折射效果会明显很多。