GeometryReady 很费性能, 所以能不用就不用
功能
GeometryReader 不是布局容器,而是一个只读测量盒 :它把父视图分配给自己的实际尺寸与坐标 通过 GeometryProxy 实时向下注入,让子视图能够:
- 按百分比计算帧大小
- 实现屏幕驱动动画(3D 旋转、视差、滑动缩放等)
- 获取全局坐标,用于拖拽、碰撞检测、滚动联动
API
swift
GeometryReader { proxy in
// 子视图
}
proxy 提供:
-
size:CGSize------ Reader 得到的确定尺寸 -
frame(in:)------ 在不同坐标系里的 frame:.local:自身坐标系.global:屏幕原点.named(id):自定义坐标空间(配合.coordinateSpace(name:)使用)
-
safeAreaInsets------ 当前 Safe Area 插值
示例
swift
struct GeometryReaderBootcamp: View {
var body: some View {
// GeometryReady 很费性能, 所以能不用就不用
// GeometryReader { geometry in
// HStack(spacing: 0) {
// Rectangle()
// .fill(.red)
// .frame(width: geometry.size.width * 0.66)
// Rectangle().fill(.blue)
// }
// .ignoresSafeArea()
// }
ScrollView(.horizontal, showsIndicators: false) {
HStack {
ForEach(0..<20) { index in
GeometryReader { geometry in
RoundedRectangle(cornerRadius: 16)
.rotation3DEffect(
Angle(degrees: getPercentage(geometry: geometry) * 40),
axis: (x: 0, y: 1.0, z: 0.0))
}
.frame(width: 320, height: 240)
.padding()
}
}
}
}
private func getPercentage(geometry: GeometryProxy) -> Double {
let maxDistance = UIScreen.main.bounds.width / 2
let currentX = geometry.frame(in: .global).midX
return Double(1 - (currentX / maxDistance))
}
}
性能与最佳实践
- 避免深层嵌套:布局阶段每帧都会重新调用闭包。
- 仅需尺寸时,优先使用
.frame(maxWidth: .infinity, maxHeight: .infinity)+ 背景图层,代替嵌套 Reader。 - 需要全局坐标再
frame(in: .global),不要一上来就放在最外层。 - 与
ScrollView、LazyVStack联用时,把 Reader 放在叶子节点,减少重算范围。 - 若只关心 Safe Area 插值,可用
.padding(.safeArea)替代 Reader。
小结
GeometryReader 是 SwiftUI 的"尺子"------不绘制、只测量 。
合理用它可实现百分比布局、视差动画、滑动联动等高级效果;
但牢记:能靠固有布局解决的,就不要让尺子上场。