求救!被 coordinateSpace 给绕晕了

2022-03-03 18:48:39 +08:00
 sillydaddy

官方对 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:)

8491 次点击
所在节点    iDev
11 条回复
sillydaddy
2022-03-03 20:38:09 +08:00
看样子大家注意力都在俄乌上了
justin2018
2022-03-03 20:38:13 +08:00
https://swiftontap.com/coordinatespace

曾经收藏的一个文档 可以看看 不确定是否对楼主有帮助
sillydaddy
2022-03-03 20:39:54 +08:00
@justin2018 谢谢。这个我看过,不过没有解决我的疑惑。
goldenlove
2022-03-04 10:03:53 +08:00
看 #2 文档貌似仅相对于父容器?
比如换成 stack 就应该正常
sillydaddy
2022-03-04 10:35:46 +08:00
@goldenlove > “看 #2 文档貌似仅相对于父容器?”

它虽然提到了 parent 之类的,但没有说必须是父容器。官方文档里面也没有说需要父容器。
而且,上面给的只是一个 Demo ,我在实际的项目里面,相对父容器得到的值也不对。
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 到全局坐标系。
minsheng
2022-03-04 12:28:17 +08:00
@sillydaddy 相对父容器得到的值不对,有可能是你视觉上理解的父容器 /子元素的位置和它的实际坐标系有偏差,比如说可能涉及了 offset 这种。或者你跟 global 坐标系的值比对一下,看看是不是依然是同一个问题。
sillydaddy
2022-03-04 17:01:55 +08:00
@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);
}
}
}
}

```
sillydaddy
2022-03-04 17:19:10 +08:00
@minsheng
@goldenlove
@justin2018

结帖。看#8 楼,应该算是发现问题的表面原因了。感叹一下,SwiftUI 虽然很简洁强大,但还是有很多坑要趟。仅仅用了它一个 List 和 coordinateSpace ,就要耗费我这么多精力。😂
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 传上来的内容。
minsheng
2022-03-04 21:42:35 +08:00
下面这个例子可以看到 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) {
}
}

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/837781

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX