sillydaddy
5.44D
4.91D
V2EX  ›  iDev

求救!被 coordinateSpace 给绕晕了

By sillydaddy at 2022 年 3 月 3 日 · 9718 次点击

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

11 条回复  •  2022-03-04 21:42:35 +08:00
sillydaddy
   1
sillydaddy  
OP
   2022 年 3 月 3 日 via Android
看样子大家注意力都在俄乌上了
justin2018
   2
justin2018  
   2022 年 3 月 3 日
https://swiftontap.com/coordinatespace

曾经收藏的一个文档 可以看看 不确定是否对楼主有帮助
sillydaddy
   3
sillydaddy  
OP
   2022 年 3 月 3 日 via Android
@justin2018 谢谢。这个我看过,不过没有解决我的疑惑。
goldenlove
   4
goldenlove  
   2022 年 3 月 4 日
看 #2 文档貌似仅相对于父容器?
比如换成 stack 就应该正常
sillydaddy
   5
sillydaddy  
OP
   2022 年 3 月 4 日
@goldenlove > “看 #2 文档貌似仅相对于父容器?”

它虽然提到了 parent 之类的,但没有说必须是父容器。官方文档里面也没有说需要父容器。
而且,上面给的只是一个 Demo ,我在实际的项目里面,相对父容器得到的值也不对。
minsheng
   6
minsheng  
   2022 年 3 月 4 日
试一下这个:

```
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
   7
minsheng  
   2022 年 3 月 4 日
@sillydaddy 相对父容器得到的值不对,有可能是你视觉上理解的父容器 /子元素的位置和它的实际坐标系有偏差,比如说可能涉及了 offset 这种。或者你跟 global 坐标系的值比对一下,看看是不是依然是同一个问题。
sillydaddy
   8
sillydaddy  
OP
   2022 年 3 月 4 日
@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
   9
sillydaddy  
OP
   2022 年 3 月 4 日
@minsheng
@goldenlove
@justin2018

结帖。看#8 楼,应该算是发现问题的表面原因了。感叹一下,SwiftUI 虽然很简洁强大,但还是有很多坑要趟。仅仅用了它一个 List 和 coordinateSpace ,就要耗费我这么多精力。😂
minsheng
   10
minsheng  
   2022 年 3 月 4 日
@sillydaddy List 底层应该是 UITableView 或者 UICollectionView ,每一个 cell 都是通过 UIHostingController 包起来的。目前来看应该是 UIHostingController 内部的 view 没有办法访问到外部的 coordinate space 。可能是一个 bug ,毕竟 EnvironmentValues 是可以跨 UIHostingController 传的;可能不是,你定义的 onPreferenceChange 其实并没有拿到 List 里的 view hierarchy 传上来的内容。
minsheng
   11
minsheng  
   2022 年 3 月 4 日   ❤️ 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) {
}
}
iOS 开发实用技术导航
NSHipster 中文版
› http://nshipster.cn/
cocos2d 开源 2D 游戏引擎
› http://www.cocos2d-iphone.org/
CocoaPods
› http://cocoapods.org/
Google Analytics for Mobile 统计解决方案
› http://code.google.com/mobile/analytics/
WWDC
› https://developer.apple.com/wwdc/
Design Guides and Resources
› https://developer.apple.com/design/
Transcripts of WWDC sessions
› http://asciiwwdc.com
Cocoa with Love
› http://cocoawithlove.com/
Cocoa Dev Central
› http://cocoadevcentral.com/
NSHipster
› http://nshipster.com/
Style Guides
› Google Objective-C Style Guide
› NYTimes Objective-C Style Guide
Useful Tools and Services
› Charles Web Debugging Proxy
› Smore
© 2026 V2EX · 33ms · 3.9.8.5