SWiftUI主播参考内部列表
我正在使用SWiftUI主播参考内部列表,swiftui,swiftui-list,geometryreader,Swiftui,Swiftui List,Geometryreader,我正在使用AnchorReferences设置GeometryReader的高度,以适应其内容的高度。我遇到的问题是,在上下滚动列表几次后,应用程序冻结,设计变得混乱,我在控制台中收到以下消息: Bound preference CGFloatPreferenceKey tried to update multiple times per frame. 有没有办法解决这个问题 重要提示:我已经尽可能地简化了我的设计 代码如下: struct ListAndPreferences: View
AnchorReferences
设置GeometryReader
的高度,以适应其内容的高度。我遇到的问题是,在上下滚动列表几次后,应用程序冻结,设计变得混乱,我在控制台中收到以下消息:
Bound preference CGFloatPreferenceKey tried to update multiple times per frame.
有没有办法解决这个问题
重要提示:我已经尽可能地简化了我的设计
代码如下:
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()
}
}
struct ListAndPreferences:视图{
var body:一些观点{
列表(1..一些视图){
VStack{
HStack{
文本(“你好”)
.padding()
.背景(颜色.紫色)
文本(“世界”)
.padding()
.背景(颜色.紫色)
}
}
.框架(宽度:maxWidth)
}
}
结构CGFloatPreferenceKey:PreferenceKey{
静态变量defaultValue:CGFloat=0
静态函数reduce(值:inout CGFloat,nextValue:()->CGFloat){
value=nextValue()
}
}
事实上,我们不知道SwiftUI在堆栈上可以呈现自定义视图的主体多少次,但首选项实际上只需要编写一次(然后应该进行转换,这更复杂)
可能的解决方案是在首选项中使用不同类型的容器,这样值就不会重新写入,而是累积
这是您代码的修改部分。使用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)
}
}
//使用字典存储每个视图的计算高度idx
结构CGFloatPreferenceKey:PreferenceKey{
静态变量defaultValue:[Int:CGFloat]=[:]
静态函数reduce(值:inout[Int:CGFloat],下一个值:()->[Int:CGFloat]){
value.merge(nextValue()){$1}
}
}
结构内部视图:视图{
@状态变量高度:CGFloat=0
变量idx:Int
var body:一些观点{
GeometryReader{中的代理
generateContent(maxWidth:proxy.frame(in:.global).size.width)
.AnchorReference(键:CGFloatPreferenceKey.self,值:Anchor.Source.bounds,转换:{Anchor in
[idx:代理[anchor]。大小。高度]
})
}
.框架(最小高度:高度)
.onPreferenceChange(CGFloatPreferenceKey.self,执行:{中的值
高度=值[idx]??.0
})
}
private func generateContent(maxWidth:CGFloat)->一些视图{
VStack{
HStack{
文本(“你好”)
.padding()
.背景(颜色.紫色)
文本(“世界”)
.padding()
.背景(颜色.紫色)
}
}
.框架(宽度:maxWidth)
}
}
您的列表中的代码试图做得太多了。SwiftUI的一个好处是您不需要手动设置视图的高度。可能简化示例时,GeometryReader
只是遗留下来的,但在这种简化的情况下,您不需要它(或首选项).这就是您所需要的:
导入快捷界面
结构列表和首选项:视图{
var body:一些观点{
列表(1..,但首选项实际上只需要编写一次
——因此“尝试每帧更新多次”这是一件坏事吗?我不认为这是坏事,这只是一个警告,意味着您存储的某些首选项值可能会丢失,而不在需要时应用。啊,这是有道理的。谢谢!谢谢@Asperi!有没有办法在InnerView中“生成”idx,这样我就不必将其作为参数发送?