观察 SwiftUI 中的框架变化
Observe frame changes in SwiftUI
我有可以拖放到其他视图之上的视图(可以说是类别)。为了检测我在哪个类别视图之上,我将它们的帧存储在一个帧数组中,这发生在它们不可见叠加层的 onAppear 中。 (这是基于 this 教程中 Paul Hudson 的实施)。
这一切都很好,除非那些视图的位置发生变化,例如在设备方向上或 windows 在 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)
}
}
)
}
}
}
因此,欢迎了解如何更新这些实例中的帧或如何以不同方式观察它们。
可以使用视图首选项在刷新期间动态读取视图帧,因此您不必关心方向,因为每次重绘视图时都有实际帧。
这是方法草案。
为视图首选项键引入模型:
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())
}
}
现在是您的代码(假设 @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 [=11=] {
categoryFrames[item.i] = item.p
}
}
}
我遇到了类似的问题,这个 post 启发了我寻找解决方案。所以也许这对其他人有用。
只需像分配给 onAppear
一样分配给 onChange
修饰符,并在 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)
}
}
)
}
}
}
我有可以拖放到其他视图之上的视图(可以说是类别)。为了检测我在哪个类别视图之上,我将它们的帧存储在一个帧数组中,这发生在它们不可见叠加层的 onAppear 中。 (这是基于 this 教程中 Paul Hudson 的实施)。
这一切都很好,除非那些视图的位置发生变化,例如在设备方向上或 windows 在 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)
}
}
)
}
}
}
因此,欢迎了解如何更新这些实例中的帧或如何以不同方式观察它们。
可以使用视图首选项在刷新期间动态读取视图帧,因此您不必关心方向,因为每次重绘视图时都有实际帧。
这是方法草案。
为视图首选项键引入模型:
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())
}
}
现在是您的代码(假设 @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 [=11=] {
categoryFrames[item.i] = item.p
}
}
}
我遇到了类似的问题,这个 post 启发了我寻找解决方案。所以也许这对其他人有用。
只需像分配给 onAppear
一样分配给 onChange
修饰符,并在 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)
}
}
)
}
}
}