🌐 一、GeometryReader 是什么?
GeometryReader 是一个视图容器,它会:
-
占据父容器能给它的全部空间;
-
在其闭包中提供一个 GeometryProxy 对象;
-
你可以通过这个 GeometryProxy 获取父布局信息,比如:
-
size(宽高)
-
safeAreaInsets
-
frame(in:)(在不同坐标空间中的位置)
-
它的基本形式如下:
GeometryReader { geometry in
// 在这里使用 geometry 来计算布局
}
🧭 二、常用的 GeometryProxy 属性
| 属性 | 类型 | 说明 |
|---|---|---|
| geometry.size | CGSize | 当前 GeometryReader 的大小(宽高) |
| geometry.safeAreaInsets | EdgeInsets | 当前区域的安全区内边距 |
| geometry.frame(in:) | CGRect | 当前 GeometryReader 在指定坐标空间中的位置和大小 |
示例:
GeometryReader { geometry in
Text("宽度:\(geometry.size.width),高度:\(geometry.size.height)")
.frame(width: geometry.size.width / 2)
.background(Color.blue)
}
.background(Color.gray)
上面这段代码会让 Text 视图的宽度占父视图的一半。
🧩 三、GeometryReader 的一个常见用途
1.相对定位布局
例如根据父视图宽高,让一个元素始终居中或相对某个位置布局:
GeometryReader { geo in
Circle()
.frame(width: 100, height: 100)
.position(x: geo.size.width / 2, y: geo.size.height / 2)
}
2.响应滚动偏移(ScrollView + GeometryReader)
可以用来检测某个视图在滚动中的位置变化:
ScrollView {
GeometryReader { geo in
let offset = geo.frame(in: .global).minY
Text("Offset: \(offset)")
.padding()
.background(Color.yellow)
}
.frame(height: 50)
}
这种技巧常用于制作滚动渐变效果、吸顶动画、折叠头图等。
3.根据容器尺寸自适应内容布局
比如动态调整字体大小、列数、间距:
GeometryReader { geo in
let columns = Int(geo.size.width / 100)
LazyVGrid(columns: Array(repeating: GridItem(.flexible()), count: columns)) {
ForEach(0..<50) { index in
Text("Item \(index)")
.frame(height: 50)
.background(Color.blue.opacity(0.3))
}
}
}
⚠️ 四、GeometryReader 的注意事项
-
它默认会“填满”父视图可用的空间。
因此很多人用时发现“布局乱了”或“撑满全屏”,这是正常行为。
如果只想让它按内容大小布局,可以包一层固定大小的容器:
GeometryReader { geo in
...
}
.frame(height: 200) // 限定高度
-
不要滥用 GeometryReader 来布局所有东西。
SwiftUI 有自己的布局系统(HStack、VStack、ZStack、Layout 等),GeometryReader 主要用于需要知道“父容器的尺寸或位置”的时候。
-
避免在动画中频繁计算 GeometryReader 的值,因为它会触发布局重绘,影响性能。
🧠 五、总结一下它的角色
| 功能 | 用途 |
|---|---|
| 读取父容器的几何信息 | size, position, safeAreaInsets |
| 参与布局计算 | 用于相对定位、自适应布局 |
| 配合滚动、动画 | 响应位置变化,实现滚动特效 |
✅ 简短总结
GeometryReader = SwiftUI 的布局测量工具。
它不是布局方式,而是让你“知道布局尺寸和位置”的工具。
常用于实现动态、相对、响应式布局。