官方对 coordinateSpace 的解释是:
Assigns a name to the view’s coordinate space, so other code can operate on dimensions like points and sizes relative to the named space.
我的理解是,用户可以给某个 view 的空间坐标取个名字,然后获取到其他 view 在指定名字空间中的相对坐标。
比如横向排列 3 个圆形。
⭕️⭕️⭕️
计算第 2 个圆形相对于第 3 个圆形的相对坐标。由于第 2 个⭕️在第三个⭕️的左侧,相对坐标的 x 值,应该是负数。
在第 2 个⭕️中,print(proxy.frame(in: .named("circle3")).origin)的输出结果总是正的。
是我对 coordinateSpace 的理解有问题吗?
var body: some View {
HStack(spacing:0) {
Circle()
.fill(.red)
.frame(width: 100, height: 100)
.coordinateSpace(name: "circle1")
GeometryReader{ proxy in
Circle()
.fill(.green)
.frame(width: 100, height: 100)
.coordinateSpace(name: "circle2")
.onTapGesture {
print(proxy.frame(in: .named("circle3")).origin);
}
}
Circle()
.fill(.blue)
.frame(width: 100, height: 100)
.coordinateSpace(name: "circle3")
}
.coordinateSpace(name: "stack")
}
看官方说明完全找不到头绪。 https://developer.apple.com/documentation/swiftui/view/coordinatespace(name:)
1
sillydaddy OP 看样子大家注意力都在俄乌上了
|
2
justin2018 2022-03-03 20:38:13 +08:00
|
3
sillydaddy OP @justin2018 谢谢。这个我看过,不过没有解决我的疑惑。
|
4
goldenlove 2022-03-04 10:03:53 +08:00
看 #2 文档貌似仅相对于父容器?
比如换成 stack 就应该正常 |
5
sillydaddy OP @goldenlove > “看 #2 文档貌似仅相对于父容器?”
它虽然提到了 parent 之类的,但没有说必须是父容器。官方文档里面也没有说需要父容器。 而且,上面给的只是一个 Demo ,我在实际的项目里面,相对父容器得到的值也不对。 |
6
minsheng 2022-03-04 12:26:11 +08:00
试一下这个:
``` var body: some View { HStack(spacing:0) { Circle() .fill(.red) .frame(width: 100, height: 100) .coordinateSpace(name: "circle1") GeometryReader{ proxy in Circle() .fill(.green) .frame(width: 100, height: 100) .coordinateSpace(name: "circle2") .onTapGesture { print(proxy.frame(in: .named("circle1")).origin) print(proxy.frame(in: .named("circle3")).origin) print(proxy.frame(in: .named("stack")).origin) print(proxy.frame(in: .global).origin) } }.frame(width: 100, height: 100) Circle() .fill(.blue) .frame(width: 100, height: 100) .coordinateSpace(name: "circle3") } .coordinateSpace(name: "stack") } ``` 看起来确实是必须父容器,不然会找不到,default 到全局坐标系。 |
7
minsheng 2022-03-04 12:28:17 +08:00
@sillydaddy 相对父容器得到的值不对,有可能是你视觉上理解的父容器 /子元素的位置和它的实际坐标系有偏差,比如说可能涉及了 offset 这种。或者你跟 global 坐标系的值比对一下,看看是不是依然是同一个问题。
|
8
sillydaddy OP @minsheng
我可能找到问题所在了:多级父容器中,如果用到了 List ,可能会导致结果不对。 比如下面的代码,把外层的 HStack 的空间坐标系命名为"parent",然后取 Text("a")相对这个坐标系的坐标,发现有问题,水平拖拽这个 HStack ,会发现取得的相对坐标在不断变化,而正常来说,相对坐标应该是固定不变的。 如果把 List 改成 VStack ,就没有这个问题。我猜测,是 List 作为 Text("a")的直接父容器,「扰乱」或者「阻隔」了取相对坐标的功能。 ``` struct XOffsetPrefercence: PreferenceKey{ static var defaultValue: Float = 0.0; static func reduce(value: inout Float, nextValue: () -> Float){ print("value=", value, ",", "nextValue=", nextValue()); value = nextValue(); } } struct ContentView: View { var body: some View { ScrollView(.horizontal){ HStack{ List{ Text("a") .overlay(content: { GeometryReader{ geoitem in Color.red.frame(width: 150, height: 2) .preference(key: XOffsetPrefercence.self, value: Float(geoitem.frame(in:.named("parent") ).minX)) } }) Text("b") }.frame(width: 200) List{ Text("c") Text("d") }.frame(width: 200) } .coordinateSpace(name: "parent") .onPreferenceChange(XOffsetPrefercence.self){ value in print("xoffset:", value); } } } } ``` |
9
sillydaddy OP @minsheng
@goldenlove @justin2018 结帖。看#8 楼,应该算是发现问题的表面原因了。感叹一下,SwiftUI 虽然很简洁强大,但还是有很多坑要趟。仅仅用了它一个 List 和 coordinateSpace ,就要耗费我这么多精力。😂 |
10
minsheng 2022-03-04 21:42:05 +08:00
@sillydaddy List 底层应该是 UITableView 或者 UICollectionView ,每一个 cell 都是通过 UIHostingController 包起来的。目前来看应该是 UIHostingController 内部的 view 没有办法访问到外部的 coordinate space 。可能是一个 bug ,毕竟 EnvironmentValues 是可以跨 UIHostingController 传的;可能不是,你定义的 onPreferenceChange 其实并没有拿到 List 里的 view hierarchy 传上来的内容。
|
11
minsheng 2022-03-04 21:42:35 +08:00 1
下面这个例子可以看到 UIHostingController 对 coordinate space 的影响:
struct ContentView: View { var body: some View { HStack { Color.blue.frame(width: 200) HStack { Color.green.frame(width: 100) InnerHost() .background { GeometryReader { proxy in let _ = print("Outer", proxy.frame(in: .named("parent")).origin) Color.clear } } }.coordinateSpace(name: "parent") } } } struct Inner: View { var body: some View { GeometryReader { proxy in let _ = print("Inner", proxy.frame(in: .named("parent")).origin) Color.red } } } struct InnerHost: UIViewControllerRepresentable { func makeUIViewController(context: Context) -> some UIViewController { UIHostingController(rootView: Inner()) } func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) { } } |