【问题标题】:SwiftUI List is extremely slow at showing actions (leading/trailing, contextMenu) when the dataset is big当数据集很大时,SwiftUI List 在显示动作(前导/尾随,contextMenu)方面非常慢
【发布时间】:2021-09-19 17:26:37
【问题描述】:

我在使用带有大量数据的 SwiftUI List 时遇到了性能问题。我创建了一个演示应用程序,只是为了展示 500_000 Strings 的问题并显示其中一个的尾随动作,CPU 在几秒钟内达到 100%,这完全无法使用。我还继续包装 UITableView 以在 SwiftUI 上使用相同的数据集(相同的 50 万 Strings)并立即显示尾随动作。

有什么方法可以在 SwiftUI List 上加快速度,或者这只是框架的限制?

我只需更改名为listKind 的变量,就可以轻松测试这两种实现,这是示例代码:

import SwiftUI

@main
struct LargeListPerformanceProblemApp: App {
    var body: some Scene {
        WindowGroup {
            NavigationView {
                ContentView().navigationBarTitleDisplayMode(.inline)
            }
        }
    }
}

enum ListKind {
    case slow
    case fast
}

struct ContentView: View {
    
    var listKind = ListKind.slow
    var items: [String]
    
    init() {
        self.items = (0...500_000).map { "Item \($0)" }
    }
    
    var body: some View {
        switch listKind {
        case .slow:
            List {
                ForEach(items, id: \.self) { item in
                    Text(item).swipeActions(edge: .trailing, allowsFullSwipe: true) {
                        Button("Print") {
                            let _ = print("Tapped")
                        }
                    }
                }
            }.navigationTitle("Slow (SwiftUI List)")
        case .fast:
            FastList(items: self.items)
                .navigationTitle("Fast (UITableView Wrapper)")
        }
    }
}


// UITableView wrapper
struct FastList: UIViewRepresentable {
    
    let items: [String]
    
    init(items: [String]) {
        self.items = items
    }
    
    func makeUIView(context: Context) -> UITableView {
        let tableView = UITableView(frame: .zero, style: .insetGrouped)
        tableView.dataSource = context.coordinator
        tableView.delegate = context.coordinator
        return tableView
    }
    
    func updateUIView(_ uiView: UITableView, context: Context) {
        uiView.reloadData()
    }
    
    func makeCoordinator() -> Coordinator {
        Coordinator(items: items)
    }
    
    class Coordinator: NSObject, UITableViewDataSource, UITableViewDelegate {
        
        var items: [String]
        
        init(items: [String]) {
            self.items = items
        }
        
        func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
            self.items.count
        }
        
        func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            let cell = UITableViewCell(style: .default, reuseIdentifier: nil)
            cell.textLabel?.text = self.items[indexPath.row]
            return cell
        }
        
        func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
            let printAction = UIContextualAction(style: .normal, title: "Print") { _, _, block in
                print("Tapped")
                block(true)
            }
            return UISwipeActionsConfiguration(actions: [printAction])
        }
    }
}

【问题讨论】:

  • 这是在模拟器上还是真机上?
  • @JordanD 它发生在模拟器和真实设备(iPhone 11 Pro)上。

标签: ios swift uitableview swiftui swiftui-list


【解决方案1】:

在仪器分析中,List 似乎为每个项目创建元数据以跟踪更改(例如插入/删除动画)。

因此,即使 List 被优化以避免创建不可见的行,它仍然具有直接 UITableView 实现不会产生的元数据开销。

List 开销的另一个演示是使用 ScrollView/LazyVStack 组合。我不建议将其作为替代方案(除了视觉差异之外,当您向下滚动列表时它会爆炸),但因为它不进行更改跟踪,所以它也会有一个相当快的初始显示。

【讨论】:

  • 感谢您花时间分析代码。您知道是否有任何方法可以防止更改跟踪以加快速度? (我可以放弃任何动画,我只会对显示 >= 100K 行的列表使用可能的修复)
  • 我不知道有任何解决方法。房间里的大象是问题:你真的需要一个包含 100K+ 行的列表吗?相反,您可以通过在用户滚动时扩展 items 数组来执行延迟加载列表。但是,如果用户真的向下滚动了 10 万行(并且可能更快),他们会招致大量的 UI 延迟。
  • 我知道这么长的列表并不常见,但它们在 AppKit/UIKit 中很好,因为正确的单元重用并且不会加载不在屏幕上的内容。我正在开发一个处理大量数据的应用程序,所有这些数据都可以搜索,理想情况下是的,所有内容都应该能够立即显示。我需要调查是否使用 Core Data 并在 NSFetchRequest 上设置适当的 fetchBatchSize 是否可以改善事情(我不确定它甚至可以与 SwiftUI 的 @FetchRequest 一起使用,我会在找到它时发布我的发现)。无论哪种方式,感谢您对此进行调查!
  • 这似乎是一个共同的话题,Lists 在大量数据下表现不佳:kean.blog/post/not-list
猜你喜欢
  • 1970-01-01
  • 2011-12-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-16
  • 1970-01-01
  • 1970-01-01
  • 2023-04-03
相关资源
最近更新 更多