如何解决当数据集很大时,SwiftUI 列表在显示操作前导/尾随、上下文菜单方面非常慢
我在使用包含大量数据的 SwiftUI List
时遇到了性能问题。我创建了一个演示应用程序只是为了展示 500_000 String
秒的问题并显示其中一个的尾随操作,cpu 达到 100% 几秒钟,这是完全无法使用的。我还继续包装 UITableView
以在 SwiftUI 上使用相同的数据集(相同的 50 万个 String
)使用它,并立即显示尾随操作。
有什么方法可以加快 SwiftUI List
上的速度,还是这只是框架的限制?
我只需更改名为 listKind
的 var 即可轻松测试这两种实现,示例代码如下:
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])
}
}
}
解决方法
在工具中进行分析,似乎 List 为每个项目创建元数据以跟踪更改(例如插入/删除动画)。
因此,即使 List 经过优化以避免创建不可见的行,它仍然具有直接 UITableView 实现不会产生的元数据开销。
List 开销的另一个演示是改为使用 ScrollView/LazyVStack 组合。我不建议将此作为替代方案(除了视觉差异之外,当您向下滚动列表时,它会爆炸),但由于它不进行更改跟踪,因此它也将具有相当快的初始显示。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。