核心摘要:在 SwiftUI 早期,GeometryReader 是获取视图尺寸的唯一选择,但它常导致布局副作用。随着 SwiftUI 的演进(尤其是 iOS 16/17/18 的更新),我们有了 onGeometryChange、visualEffect 等性能更好、副作用更小的替代方案。
在 SwiftUI 中,动态获取视图的尺寸是一个常见需求。本文将梳理截至 2025 年主流的 4 种获取尺寸的方法,帮助你根据场景选择最佳方案。
1. 通用方案:GeometryReader
GeometryReader 是适配性最强的方式,适用于所有版本的 SwiftUI。为了避免它抢占空间破坏布局,最佳实践是将其结合 background 或 overlay 使用。
示例代码
blueRectangle
.background(
GeometryReader { proxy in
Color.clear // 创建与主视图尺寸一致的透明视图
.task(id: proxy.size) {
size = proxy.size // 获取尺寸并监听变化
}
}
)
特点:
- 优点:兼容性最好,支持所有 iOS 版本。
- 缺点:语法啰嗦,如果直接包裹视图会导致视图充满父容器。
2. 现代标准:onGeometryChange (推荐)
从 iOS 16 开始引入,并在 iOS 18 得到增强。这是目前获取视图尺寸最推荐的方式,它纯粹用于监听,不会像 GeometryReader 那样影响布局结构。
示例代码
struct SizeDemo: View {
@State var size: CGSize?
var body: some View {
Rectangle()
// iOS 16+ 基础用法
.onGeometryChange(for: CGSize.self) { proxy in
proxy.size
} action: { newValue in
size = newValue
}
}
}
iOS 18+ 增强特性
iOS 18 增加了对旧值的支持,方便处理动画或过渡逻辑:
.onGeometryChange(for: CGSize.self) { proxy in
proxy.size
} action: { old, new in
print("Size changed from \(old) to \(new)")
}
3. 视觉特效专用:visualEffect
如果你的目的是根据尺寸调整视觉效果(如 offset、scale、blur),而不是为了改变布局流,那么 iOS 17 引入的 visualEffect 是性能最高的选择。它不需要通过 @State 触发布局刷新。
示例代码
struct EffectDemo: View {
var body: some View {
Rectangle()
.foregroundStyle(.red)
.visualEffect { content, proxy in
// 直接使用 proxy 获取尺寸,无需 State 绑定
content.offset(y: proxy.size.height / 3)
}
}
}
效果
4. 容器相对布局:containerRelativeFrame
containerRelativeFrame (iOS 17+) 是处理“网格类”或“分屏类”布局的神器。它不直接告诉你尺寸数值,而是让你声明“我想要占容器宽度的几分之几”。
示例代码
以下代码将矩形设置为父容器(如 Window 或 ScrollView)宽度的 1/2 和高度的 1/4:
struct TransformsDemo: View {
var body: some View {
Rectangle()
.containerRelativeFrame([.horizontal, .vertical]) { length, axis in
if axis == .vertical {
return length / 4
} else {
return length / 2
}
}
}
}
效果与应用
它会自动向上查找最近的特定容器(如 NavigationStack 或 ScrollView)。
在 ScrollView 中实现“一屏显示三个卡片”的效果非常简单:
Rectangle()
.containerRelativeFrame(.horizontal, count: 3, span: 1, spacing: 10)
总结:如何选择?
| 方法 | 最低版本 | 适用场景 | 推荐指数 |
|---|---|---|---|
| onGeometryChange | iOS 16+ | 首选。需要获取尺寸数值更新 State 时。 | ⭐⭐⭐⭐⭐ |
| visualEffect | iOS 17+ | 仅需调整视觉效果(偏移、缩放),无需触发布局更新。 | ⭐⭐⭐⭐ |
| containerRelativeFrame | iOS 17+ | 需基于父容器(屏幕/滚动视图)按比例布局时。 | ⭐⭐⭐⭐ |
| GeometryReader | iOS 13+ | 维护旧项目,或需要获取坐标空间(CoordinateSpace)信息时。 | ⭐⭐ |