观察 SwiftUI 中的框架变化

hos*_*shy 4 swiftui geometryreader

我有可以拖放到其他视图(比如说类别)之上的视图。为了检测我位于哪个类别视图之上,我将它们的帧存储在帧数组中,这发生在它们不可见覆盖层的 onAppear 中。(这是基于教程中 Paul Hudson 的实现)。

这一切都很好,除非这些视图的位置发生变化,例如在设备方向或 iPad 上调整窗口大小时。这当然不会触发 onAppear,因此帧不再匹配。

HStack() {
ForEach(categories) { category in
    ZStack {
        Circle()
        Rectangle()
            .foregroundColor(.clear)
            .overlay(
                GeometryReader { geo in
                    Color.clear
                        .onAppear {
                            categoryFrames[index(for: category)] = geo.frame(in: .global)
                        }
                }
            )
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

因此,任何关于如何在这些情况下更新帧或如何以不同方式观察它们的想法都将受到欢迎。

ale*_*ira 11

我遇到了类似的问题,这篇文章激励我寻找解决方案。所以也许这对其他人有用。只需按照onChange与您所做的相同的方式分配给修改器onAppear,并将其设置为在geo.size更改时触发。

HStack() {
ForEach(categories) { category in
    ZStack {
        Circle()
        Rectangle()
            .foregroundColor(.clear)
            .overlay(
                GeometryReader { geo in
                    Color.clear
                        .onAppear {
                            categoryFrames[index(for: category)] = geo.frame(in: .global)
                        }
                        .onChange(of: geo.size) { _ in
                            categoryFrames[index(for: category)] = geo.frame(in: .global)
                        }
                }
            )
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


Asp*_*eri 5

可以使用视图首选项在刷新期间动态读取视图帧,因此您不必关心方向,因为每次重绘视图时都有实际的帧。

这是一个方法草案。

引入视图偏好键的模型:

struct ItemRec: Equatable {
    let i: Int        // item index
    let p: CGRect     // item position frame
}

struct ItemPositionsKey: PreferenceKey {
    typealias Value = [ItemRec]
    static var defaultValue = Value()
    static func reduce(value: inout Value, nextValue: () -> Value) {
        value.append(contentsOf: nextValue())
    }
}
Run Code Online (Sandbox Code Playgroud)

现在你的代码(假设@State private var categoryFrames = [Int, CGRect]()

HStack() {
ForEach(categories) { category in
    ZStack {
        Circle()
        Rectangle()
            .foregroundColor(.clear)
            .background(        // << prefer background to avoid any side effect
                GeometryReader { geo in
                    Color.clear.preference(key: ItemPositionsKey.self,
                        value: [ItemRec(i: index(for: category), p: geo.frame(in: .global))])
                }
            )
        }
    }
    .onPreferenceChange(ItemPositionsKey.self) {
        // actually you can use this listener at any this view hierarchy level
        // and possibly use directly w/o categoryFrames state
        for item in $0 {
           categoryFrames[item.i] = item.p
        }
    }

}
Run Code Online (Sandbox Code Playgroud)