TL;DR: 在 SwiftUI 中,使用
GeometryReader、onGeometryChange、visualEffect或containerRelativeFrame,可动态获取视图尺寸以实现响应式布局。
背景
在 SwiftUI 中,动态获取视图的尺寸是一个常见需求,尤其是在需要响应尺寸变化或调整布局的场景中。本文将介绍几种获取视图尺寸的常用方法及其适用场景。
使用 GeometryReader
GeometryReader 是适配性最强的方式,适用于所有版本的 SwiftUI。通常结合 overlay 或 background 使用,以避免影响主视图的尺寸。
示例代码
blueRectangle
.background(
GeometryReader { proxy in
Color.clear // 创建与主视图尺寸一致的透明视图
.task(id: proxy.size) {
size = proxy.size // 获取尺寸并监听变化
}
}
)特点
- 将获取尺寸的逻辑封装在
background,不影响主视图布局。 - 使用
task(id:)确保视图加载时立即获取尺寸,并在proxy.size变化时更新。
使用 onGeometryChange
从 iOS 16 开始,onGeometryChange 提供了更简洁的方式来监听尺寸变化。
示例代码
struct SizeDemo: View {
@State var size: CGSize?
var body: some View {
Rectangle()
.onGeometryChange(for: CGSize.self) { proxy in
proxy.size
} action: {
size = $0
}
}
}iOS 18+ 新特性
支持同时获取新旧尺寸值,方便处理尺寸变化逻辑。
.onGeometryChange(for: CGSize.self) { proxy in
proxy.size
} action: { old, new in
size = new // 获取新尺寸
print("Old size: \(old), New size: \(new)")
}使用 visualEffect
如果目标是根据视图尺寸应用视觉效果(如 offset、scaleEffect 等),visualEffect (iOS 17+) 提供了更直接的方式。
示例代码
struct SizeDemo: View {
var body: some View {
Rectangle()
.foregroundStyle(.red)
.visualEffect { effect, proxy in
effect
.offset(y: proxy.size.height / 3) // 视图向下偏移自身高度的 1/3
}
}
}效果
使用 containerRelativeFrame
containerRelativeFrame (iOS 17+) 是 GeometryReader 与 frame 的结合体,用于获取当前视图所在的特定容器(如窗口、NavigationStack、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
}
}
}
}containerRelativeFrame将沿Rectangle向上寻找第一个满足条件的容器,当前为窗口
效果
容器适应性
containerRelativeFrame 会自动选择合适的容器。例如,当矩形放置在 NavigationStack 中时,计算的尺寸基于 NavigationStack 的尺寸。
struct TransformsDemo: View {
var body: some View {
NavigationStack {
Rectangle()
.containerRelativeFrame([.horizontal, .vertical]) { length, axis in
if axis == .vertical {
return length / 4
} else {
return length / 2
}
}
}
.frame(width: 300, height: 300) // 指定 NavigationStack 的尺寸
.border(.red, width: 4)
}
}效果
ScrollView 中的应用
containerRelativeFrame 还能用来动态调整滚动视图中的子视图尺寸。例如,将子视图宽度设置为滚动视图宽度的 1/3:
struct ScrollViewDemo: View {
var body: some View {
ScrollView(.horizontal) {
HStack(spacing: 10) {
ForEach(0..<10) { _ in
Rectangle()
.fill(.purple)
.aspectRatio(3 / 2, contentMode: .fit)
.containerRelativeFrame(.horizontal, count: 3, span: 1, spacing: 0)
}
}
}
}
}效果
方法对比
| 方法 | 适用场景 | 特点 |
|---|---|---|
GeometryReader | 适用于所有 SwiftUI 版本 | 灵活性强,但需要开发者构建更多的代码 |
onGeometryChange | iOS 16+ 简化监听尺寸变化 | 语义清晰,支持监听新旧尺寸(iOS 18+) |
visualEffect | iOS 17+ 用于动态调整渲染效果 | 简洁高效,直接使用视图的 GeometryProxy 信息 |
containerRelativeFrame | iOS 17+ 适用于相对容器计算的场景,如窗口、滚动视图等 | 自动匹配容器上下文,便于对子视图进行动态尺寸调整 |
延伸阅读
"加入我们的 Discord 社区,与超过 2000 名苹果生态的中文开发者一起交流!"