列表中的 SWiftUI anchorPreference [英] SWiftUI anchorPreference inside List
问题描述
我正在使用 anchorPreferences
来设置 GeometryReader
的 height
以适合其内容的 height
.我遇到的问题是,在 List
上下滚动几次后,应用程序冻结,设计变得混乱,并且我在控制台中收到以下消息:
I'm using anchorPreferences
to set the height
of GeometryReader
to fit the height
of its content. The issue I'm experiencing is, after scrolling up and down the List
a few times, the app freezes, the design gets messy, and I get the following message in the console:
Bound preference CGFloatPreferenceKey tried to update multiple times per frame.
有什么想法可以解决这个问题吗?
Any ideas how I can fix this?
重要提示:我已经尽可能地简化了我的设计.
IMPORTANT NOTE: I've simplified my design as much as I could.
代码如下:
struct ListAndPreferences: View {
var body: some View {
List(1..<35) { idx in
HStack {
Text("idx: \(idx)")
InnerView(idx: idx)
}
}
}
}
struct InnerView: View {
@State var height: CGFloat = 0
var idx: Int
var body: some View {
GeometryReader { proxy in
generateContent(maxWidth: proxy.frame(in: .global).size.width)
.anchorPreference(key: CGFloatPreferenceKey.self, value: Anchor<CGRect>.Source.bounds, transform: { anchor in
proxy[anchor].size.height
})
}
.frame(height: height)
.onPreferenceChange(CGFloatPreferenceKey.self, perform: { value in
height = value
})
}
private func generateContent(maxWidth: CGFloat) -> some View {
VStack {
HStack {
Text("hello")
.padding()
.background(Color.purple)
Text("world")
.padding()
.background(Color.purple)
}
}
.frame(width: maxWidth)
}
}
struct CGFloatPreferenceKey: PreferenceKey {
static var defaultValue: CGFloat = 0
static func reduce(value: inout CGFloat , nextValue: () -> CGFloat) {
value = nextValue()
}
}
推荐答案
实际上我们不知道 SwiftUI 可以在堆栈上渲染多少次自定义视图的主体,但是首选项确实只需要编写一次(然后它们应该被转换,这更复杂).
Actually we don't know how many times on stack SwiftUI can render a body of our custom view, but preferences really required to be written only once (and then they should be transformed, which is more complex).
可能的解决方案是在 Preferences 中使用不同类型的容器,这样值不会被重写而是累积.
The possible solution is to use different type of container in Preferences, so values not re-written but accumulated.
这是您的代码的修改部分.使用 Xcode 12.1/iOS 14.1 测试
Here is modified parts of your code. Tested with Xcode 12.1 / iOS 14.1
// use dictionary to store calculated height per view idx
struct CGFloatPreferenceKey: PreferenceKey {
static var defaultValue: [Int: CGFloat] = [:]
static func reduce(value: inout [Int: CGFloat] , nextValue: () -> [Int: CGFloat]) {
value.merge(nextValue()) { $1 }
}
}
struct InnerView: View {
@State var height: CGFloat = 0
var idx: Int
var body: some View {
GeometryReader { proxy in
generateContent(maxWidth: proxy.frame(in: .global).size.width)
.anchorPreference(key: CGFloatPreferenceKey.self, value: Anchor<CGRect>.Source.bounds, transform: { anchor in
[idx: proxy[anchor].size.height]
})
}
.frame(minHeight: height)
.onPreferenceChange(CGFloatPreferenceKey.self, perform: { value in
height = value[idx] ?? .zero
})
}
private func generateContent(maxWidth: CGFloat) -> some View {
VStack {
HStack {
Text("hello")
.padding()
.background(Color.purple)
Text("world")
.padding()
.background(Color.purple)
}
}
.frame(width: maxWidth)
}
}
这篇关于列表中的 SWiftUI anchorPreference的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!