列表中的 SWiftUI anchorPreference [英] SWiftUI anchorPreference inside List

查看:59
本文介绍了列表中的 SWiftUI anchorPreference的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用 anchorPreferences 来设置 GeometryReaderheight 以适合其内容的 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆