V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
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
sillydaddy
V2EX  ›  iDev

求救!被 coordinateSpace 给绕晕了

  •  1
     
  •   sillydaddy · 2022-03-03 18:48:39 +08:00 · 8666 次点击
    这是一个创建于 1003 天前的主题,其中的信息可能已经有所发展或是发生改变。

    官方对 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-03-03 20:38:09 +08:00 via Android
    看样子大家注意力都在俄乌上了
    justin2018
        2
    justin2018  
       2022-03-03 20:38:13 +08:00
    https://swiftontap.com/coordinatespace

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

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

    结帖。看#8 楼,应该算是发现问题的表面原因了。感叹一下,SwiftUI 虽然很简洁强大,但还是有很多坑要趟。仅仅用了它一个 List 和 coordinateSpace ,就要耗费我这么多精力。😂
    minsheng
        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 传上来的内容。
    minsheng
        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) {
    }
    }
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2707 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 09:06 · PVG 17:06 · LAX 01:06 · JFK 04:06
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.