液态玻璃设计系统 (iOS 26)
实现 Apple 液态玻璃(Liquid Glass)的模式 —— 一种动态材质,可模糊背后的内容,反射周围内容的颜色和光线,并对触摸和指针交互做出响应。涵盖 SwiftUI、UIKit 和 WidgetKit 集成。
何时激活
- 构建或更新 iOS 26+ 应用,采用新的设计语言
- 实现玻璃风格的按钮、卡片、工具栏或容器
- 创建玻璃元素之间的形变过渡
- 为小组件应用液态玻璃效果
- 将现有的模糊/材质效果迁移到新的液态玻璃 API
核心模式 —— SwiftUI
基础玻璃效果
为任意视图添加液态玻璃的最简单方式:
Text("Hello, World!")
.font(.title)
.padding()
.glassEffect() // 默认:常规变体,胶囊形状
自定义形状和色调
Text("Hello, World!")
.font(.title)
.padding()
.glassEffect(.regular.tint(.orange).interactive(), in: .rect(cornerRadius: 16.0))
关键自定义选项:
.regular—— 标准玻璃效果.tint(Color)—— 添加颜色色调以突出显示.interactive()—— 对触摸和指针交互做出响应- 形状:
.capsule(默认)、.rect(cornerRadius:)、.circle
玻璃按钮样式
Button("Click Me") { /* 操作 */ }
.buttonStyle(.glass)
Button("Important") { /* 操作 */ }
.buttonStyle(.glassProminent)
多个元素的 GlassEffectContainer
始终将多个玻璃视图包裹在容器中,以获得更好的性能和形变:
GlassEffectContainer(spacing: 40.0) {
HStack(spacing: 40.0) {
Image(systemName: "scribble.variable")
.frame(width: 80.0, height: 80.0)
.font(.system(size: 36))
.glassEffect()
Image(systemName: "eraser.fill")
.frame(width: 80.0, height: 80.0)
.font(.system(size: 36))
.glassEffect()
}
}
spacing 参数控制合并距离 —— 距离越近的元素,其玻璃形状融合程度越高。
合并玻璃效果
使用 glassEffectUnion 将多个视图合并为一个玻璃形状:
@Namespace private var namespace
GlassEffectContainer(spacing: 20.0) {
HStack(spacing: 20.0) {
ForEach(symbolSet.indices, id: \.self) { item in
Image(systemName: symbolSet[item])
.frame(width: 80.0, height: 80.0)
.glassEffect()
.glassEffectUnion(id: item < 2 ? "group1" : "group2", namespace: namespace)
}
}
}
形变过渡
在玻璃元素出现/消失时创建平滑形变:
@State private var isExpanded = false
@Namespace private var namespace
GlassEffectContainer(spacing: 40.0) {
HStack(spacing: 40.0) {
Image(systemName: "scribble.variable")
.frame(width: 80.0, height: 80.0)
.glassEffect()
.glassEffectID("pencil", in: namespace)
if isExpanded {
Image(systemName: "eraser.fill")
.frame(width: 80.0, height: 80.0)
.glassEffect()
.glassEffectID("eraser", in: namespace)
}
}
}
Button("Toggle") {
withAnimation { isExpanded.toggle() }
}
.buttonStyle(.glass)
水平滚动扩展到侧边栏下方
要使水平滚动内容延伸到侧边栏或检查器下方,请确保 ScrollView 内容到达容器的前缘/后缘。当布局延伸到边缘时,系统会自动处理侧边栏下方的滚动行为 —— 无需添加额外的修饰符。
核心模式 —— UIKit
基础 UIGlassEffect
let glassEffect = UIGlassEffect()
glassEffect.tintColor = UIColor.systemBlue.withAlphaComponent(0.3)
glassEffect.isInteractive = true
let visualEffectView = UIVisualEffectView(effect: glassEffect)
visualEffectView.translatesAutoresizingMaskIntoConstraints = false
visualEffectView.layer.cornerRadius = 20
visualEffectView.clipsToBounds = true
view.addSubview(visualEffectView)
NSLayoutConstraint.activate([
visualEffectView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
visualEffectView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
visualEffectView.widthAnchor.constraint(equalToConstant: 200),
visualEffectView.heightAnchor.constraint(equalToConstant: 120)
])
// 向 contentView 添加内容
let label = UILabel()
label.text = "Liquid Glass"
label.translatesAutoresizingMaskIntoConstraints = false
visualEffectView.contentView.addSubview(label)
NSLayoutConstraint.activate([
label.centerXAnchor.constraint(equalTo: visualEffectView.contentView.centerXAnchor),
label.centerYAnchor.constraint(equalTo: visualEffectView.contentView.centerYAnchor)
])
多个元素的 UIGlassContainerEffect
let containerEffect = UIGlassContainerEffect()
containerEffect.spacing = 40.0
let containerView = UIVisualEffectView(effect: containerEffect)
let firstGlass = UIVisualEffectView(effect: UIGlassEffect())
let secondGlass = UIVisualEffectView(effect: UIGlassEffect())
containerView.contentView.addSubview(firstGlass)
containerView.contentView.addSubview(secondGlass)
滚动边缘效果
scrollView.topEdgeEffect.style = .automatic
scrollView.bottomEdgeEffect.style = .hard
scrollView.leftEdgeEffect.isHidden = true
工具栏玻璃集成
let favoriteButton = UIBarButtonItem(image: UIImage(systemName: "heart"), style: .plain, target: self, action: #selector(favoriteAction))
favoriteButton.hidesSharedBackground = true // 退出共享玻璃背景
核心模式 —— WidgetKit
渲染模式检测
struct MyWidgetView: View {
@Environment(\.widgetRenderingMode) var renderingMode
var body: some View {
if renderingMode == .accented {
// 着色模式:白色着色,主题玻璃背景
} else {
// 全彩模式:标准外观
}
}
}
视觉层次的强调色分组
HStack {
VStack(alignment: .leading) {
Text("Title")
.widgetAccentable() // 强调色组
Text("Subtitle")
// 主要组(默认)
}
Image(systemName: "star.fill")
.widgetAccentable() // 强调色组
}
着色模式中的图像渲染
Image("myImage")
.widgetAccentedRenderingMode(.monochrome)
容器背景
VStack { /* 内容 */ }
.containerBackground(for: .widget) {
Color.blue.opacity(0.2)
}
关键设计决策
| 决策 | 理由 |
|---|---|
| GlassEffectContainer 包裹 | 性能优化,支持玻璃元素之间的形变 |
spacing 参数 | 控制合并距离 —— 精细调整元素之间需要多近才能融合 |
@Namespace + glassEffectID | 在视图层次结构变化时启用平滑形变过渡 |
interactive() 修饰符 | 显式选择触摸/指针反应 —— 并非所有玻璃都应该响应 |
| UIKit 中的 UIGlassContainerEffect | 与 SwiftUI 相同的容器模式,保持一致性 |
| 小组件中的着色渲染模式 | 当用户选择着色主屏幕时,系统应用着色玻璃效果 |
最佳实践
- 始终使用 GlassEffectContainer 在对多个同级视图应用玻璃效果时 —— 它支持形变并提高渲染性能
- 在其他外观修饰符(frame、font、padding)之后 应用
.glassEffect() - 仅在响应用户交互的元素(按钮、可切换项)上 使用
.interactive() - 在容器中仔细选择 spacing 以控制玻璃效果何时合并
- 更改视图层次结构时使用
withAnimation以启用平滑形变过渡 - 在不同外观下测试 —— 浅色模式、深色模式以及着色/强调色模式
- 确保无障碍对比度 —— 玻璃上的文本必须保持可读性
应避免的反模式
- 不使用 GlassEffectContainer 而使用多个独立的
.glassEffect()视图 - 嵌套过多的玻璃效果 —— 会降低性能和视觉清晰度
- 对每个视图都应用玻璃效果 —— 仅保留给交互式元素、工具栏和卡片
- 在 UIKit 中使用圆角时忘记
clipsToBounds = true - 忽略小组件中的着色渲染模式 —— 会破坏着色主屏幕外观
- 在玻璃后面使用不透明背景 —— 会抵消半透明效果
使用时机
- 使用 iOS 26 新设计的导航栏、工具栏和标签栏
- 浮动操作按钮和卡片式容器
- 需要视觉深度和触摸反馈的交互式控件
- 应与系统液态玻璃外观集成的小组件
- 相关 UI 状态之间的形变过渡