0

感谢您帮助我解决以下问题。

总体目标

我在一个 SwiftUI 视图上工作,它包含一个可滚动的区域来呈现一些数据。假设它应该显示一定范围内的自然数,例如 0 到 2,000,000。每个数字都显示在跨越一半屏幕的自定义数字视图中。可滚动区域包含按升序排列的数量视图。

约束如下:

  1. 数字视图是延迟加载的。
  2. 我们可以定义加载可滚动区域时首先显示的数字视图。
  3. 在某些时候,可滚动视图必须忘记超出范围的视图以释放内存。前两个约束强加:我们可以加载视图并显示第 1,000,000 个数字视图,而无需加载之前的 999,999 个数字视图。

第一约束

满足第一个约束很简单。我们创建一个ScrollView嵌套 a LazyVStack, aForEach以使数据可用和数字视图。如果我们显示我们的视图,打印语句只显示前几个视图被加载。

var body: some View {
    ScrollView(.vertical, showsIndicators: false) {
        LazyVStack {
            ForEach(0..<2000000) { number in
                NumberView(number)
            }
        }
    }
}
struct NumberView: View {
    
    let number: Int
    
    init(_ number: Int) {
        self.number = number
        
        print("init: \(number)")
    }
    
    var body: some View {
        print("\(number)")
        
        return Text("\(number)")
            .frame(height: 300)
            .border(.red)
    }
}

第二个约束

我们修改之前的代码。因此,我们将 嵌入ScrollView到 aScrollViewReader中并用 ID 标记数字视图。现在,一旦出现,我们就可以滚动到所需的数字视图ScrollView

var body: some View {
    ScrollViewReader { proxy in
        ScrollView(.vertical, showsIndicators: false) {
            LazyVStack {
                ForEach(0..<2000000) { number in
                    NumberView(number)
                        .id(number)
                }
            }
        }
        .onAppear {
            proxy.scrollTo(1000000, anchor: .top)
        }
    }
}

但是现在我们违反了我们的第一个约束。当滚动到第 1000000 个数字视图时,我们必须初始化之前的 999,999 个。这有两个缺点:它需要大量的时间和内存。最终导致内存分配失败。也许,取消分配数字视图是一种选择。但是我们仍然必须初始化它们以便以后丢弃它们,这需要时间。

第三个约束

我不能 100% 确定 SwiftUI 是否或在什么情况下会在无法访问的情况下取消分配 number-view-structs。onDisappear我假设一旦调用了它们的方法,它们就可以从内存中删除。

替代解决方案

我读了一篇关于无限滚动列表的文章。这篇文章没有解决完全相同的问题。但它建议ForEach根据显示的数字视图修改迭代列表。onAppear我们可以使用和来跟踪显示的数字视图onDisappear。我们可以使用这两个函数来更新列表中加载的数字。

struct InfiniteList: View {
    
    @StateObject var state: Data = Data()
    
    var body: some View {
        ScrollView {
            LazyVStack {
                ForEach(state.list, id: \.self) { number in
                    NumberView(number)
                        .onAppear {
                            // load more data
                        }
                        .onDisappear {
                            // unload data
                        }
                }
            }
        }
    }
}

class Data: ObservableObject {
    
    // list that holds the nearby numbers
    @Published var list: [Int] = [1000000]
    
}

但是一旦我们更新列表,ScrollView 就会显示最小数字的数字视图,触发它的onAppear功能,因此会加载更小的数字。

我认为我们可以使最后一种方法起作用:

  1. 禁用更新号码列表
  2. 滚动到实际视图
  3. 再次启用更新

但这会很复杂,我们必须滚动用户在更新之前看到的确切位置。

4

0 回答 0