SwiftUI
中的GeometryReader
是一个视图,使用它我们可以很容易地访问父视图的大小和位置,并使用这些信息来创建一个响应式布局,以适应不同的设备和方向。
在本文中,我们将探索使用GeometryReader
的好处,并提供一些如何在项目中使用它的示例。
基本使用
在GeometryReader
的闭包中提供了一个GeometryProxy
的变量,用于访问容器视图的大小和空间坐标的代理。
struct GeometryReaderDemo: View {
var body: some View {
GeometryReader { geometry in
VStack(spacing: 10) {
Text("GeometryReader")
.font(.title)
Text("geometry size: \(geometry.size)")
.font(.subheadline)
HStack(spacing: 0) {
Button("Button 1") {}
.frame(width: geometry.size.width/3, height: 50)
.background(.orange)
Button("Button 2") {}
.frame(width: geometry.size.width/3, height: 50)
.background(.yellow)
Button("Button 3") {}
.frame(width: geometry.size.width/3, height: 50)
.background(.green)
}
}
}
}
}
上面的代码中,讲得到的父视图宽度三分,分别设置给了三个Button。
在比如下面这个,我们可以给GeometryReader
设置任意宽度,其内部的圆形直径永远都是GeometryReader
宽度的一半,根本不用在调整Circle
的尺寸。
还有一点值得提一下,先看下面这个图:
上面代码中竖向显示了3个Text
,对三个Text
的父视图VStack
加了颜色,以及GeometryReader
也加了背景颜色。很明显能看得出来GeometryReader
的尺寸是全屏尺寸,不过这是一个首选大小,而不是绝对大小,具体还是取决于GeometryReader
的父视图,我们可以显示地给GeometryReader
添加frame
修饰符。
另外在GeometryReader
内部,所有的组件都是左上角到右下角布局的,这个和我们在UIKit
中的坐标系很像。GeometryReader
和SwiftUI
中的其他组件的布局不太一样。
很多时候GeometryReader
中放了Color.clear
,因为颜色会自动填充父视图的大小,所以GeometryReader
也是其父视图的大小,将这个组合放到ScrollView
的background
修饰符中,可以计算一些偏移量,内容总尺寸什么的,比如在这篇文章中就涉及到了,这里就不过多说明了。
coordinates 理解与使用
除了简单的size
属性,GeometryProxy
还提供了一个frame(in:)
方法,该方法返回视图在指定坐标空间的frame数据。
func frame(in coordinateSpace: CoordinateSpace) -> CGRect
CoordinateSpace
主要有三种:
- 全局坐标空间:相对于视图层次结构根的全局坐标空间。也就是相对于整个屏幕。
- 局部坐标空间:相对于当前视图的局部坐标空间。。
- 自定义坐标空间:将
coordinateSpace()
修饰符附加到视图上来创建自定义坐标空间——该视图的任何子视图都可以读取相对于该坐标空间的frame。
可能不太好理解,看看下面的示例,有助于理解,首先明确一点,frame(in:)
方法返回的是调用者GeometryProxy
实例对象的frame
信息,也就是GeometryReader
的frame
信息。
var body: some View {
// 上面红色 高度100
VStack(spacing: 0) {
Color.red
.frame(height: 100)
HStack(spacing: 0) {
// 左侧蓝色 宽度100
Color.blue
.frame(width: 100)
// 中间橘黄色
VStack(spacing: 0) {
// GeometryReader 绿色
GeometryReader { proxy in
VStack(spacing: 0) {
Color.green
.onTapGesture {
printFrameMessage(proxy: proxy)
}
}
}
.background(.orange)
.padding(50) // GeometryReader四边缩进50.
}
.background(Color.orange)
.coordinateSpace(name: "Custom")
// 右侧蓝色 宽度100
Color.blue
.frame(width: 100)
}
// 下面红色 高度100
Color.red
.frame(height: 100)
}
}
private func printFrameMessage(proxy: GeometryProxy) {
print("Screen size: \(UIScreen.main.bounds.size)")
print("Global center: \(proxy.frame(in: .global).midX) x \(proxy.frame(in: .global).midY)")
print("Custom center: \(proxy.frame(in: .named("Custom")).midX) x \(proxy.frame(in: .named("Custom")).midY)")
print("Local center: \(proxy.frame(in: .local).midX) x \(proxy.frame(in: .local).midY)")
}
上面代码中,绿色部分为GeometryReader
,并添加了点击事件,点击后显示出该GeometryReader
相对于三个坐标空间的中心点坐标信息。
整个屏幕为global
坐标空间。
橘黄色部分为自定义坐标空间,代码中添加了.coordinateSpace(name: "Custom")
。
绿色部分为local
坐标空间。
关于各颜色块的尺寸代码中有标注。
点击绿色区域后,打印出的信息如下,各数据已在上图中有标出,一目了然。
Screen size: (393.0, 852.0)
Global center: 196.5 x 438.5
Custom center: 96.5 x 279.5
Local center: 46.5 x 229.5
写在最后
本文主要介绍了GeometryReader
的用法,以及使用GeometryProxy
读取frame
和coordinates
数据,介绍了三种坐标系,文中所有代码都测试过,如果有不正确的地方,还望大家指正。
最后,希望能够帮助到有需要的朋友,如果觉得有帮助,还望点个赞,添加个关注,笔者也会不断地努力,写出更多更好用的文章。