SwiftUI表格数据量变更时动画性能低下的优化求助
问题
我的应用里,侧边栏是表格的数据筛选器,详情视图展示表格本身。在侧边栏选择筛选项时,表格视觉更新存在明显延迟。通过性能分析发现问题出在表格行高的计算上。我尝试使用.fixedSize(horizontal: false, vertical: true)固定所有表格列视图的高度,但并未解决问题。
由于所有行的高度是预先已知且一致的,是否存在强制表格不重新计算行高的方法?
模型代码
@Observable final class Item: Identifiable { var id: UUID var value: Int var name: String var c1: String var c2: String var c3: String var c4: String var c5: String var c6: String var c7: String var c8: String var c9: String init(value: Int) { self.id = UUID() self.value = value self.name = "name_" + "\(Int.random(in: 0...9))" self.c1 = "\(UUID())" self.c2 = "\(UUID())" self.c3 = "\(UUID())" self.c4 = "\(UUID())" self.c5 = "\(UUID())" self.c6 = "\(UUID())" self.c7 = "\(UUID())" self.c8 = "\(UUID())" self.c9 = "\(UUID())" } }
@MainActor @Observable final class AppModel { var items: [Item] var selectedItem1: Set<UUID> = .init() var selectedFilters: Set<String> = [] var subject: CurrentValueSubject<Int, Never> = .init(0) init() { var items: [Item] = [] for i in 0...1000 { items.append(Item(value: i)) } self.items = items } func updateValue() { let index = Int.random(in: 0..<items.count) items[index].value = Int.random(in: 200...300) } func startPolling() { // 模拟网络轮询 Task { let asymcSequence = subject .debounce(for: .seconds(1), scheduler: DispatchQueue.global()) .values for await _ in asymcSequence { updateValue() subject.send(subject.value + 1) } } } }
视图代码
struct ContentView: View { @Environment(AppModel.self) private var appModel @State private var sortOrder = [KeyPathComparator(\Item.name)] private var sideBarItems: [String] { let set = Set(appModel.items.map({ $0.name })).sorted(by: <) return Array(set) } private var items: [Item] { appModel.items.filter({ appModel.selectedFilters.contains($0.name )}) } var body: some View { @Bindable var appModel = appModel NavigationSplitView { List(selection: $appModel.selectedFilters) { ForEach(sideBarItems, id: \.self) { item in Text(item) .tag(item) .badge(appModel.items.filter({ $0.name == item }).count) } } } detail: { Table(of: Item.self, selection: $appModel.selectedItem1, sortOrder: $sortOrder) { TableColumn("value") { item in Text("\(item.value)") //.fixedSize(horizontal: false, vertical: true) } TableColumn("c1", value: \.c1) TableColumn("c2", value: \.c2) TableColumn("c3", value: \.c3) TableColumn("c4", value: \.c4) TableColumn("c5", value: \.c5) TableColumn("c6", value: \.c6) TableColumn("c7", value: \.c7) TableColumn("c8", value: \.c8) TableColumn("c9", value: \.c9) } rows: { ForEach(items) } } .animation(.smooth, value: items.count) .frame(minWidth: 1000, minHeight: 640) .padding() .onAppear { appModel.startPolling() } } }
解决方案
1. 强制表格使用固定行高
SwiftUI的Table默认会自动计算每一行的高度,要跳过这个计算,可以给每个TableRow设置固定高度,并移除默认内边距避免高度偏差:
// 修改表格的rows部分 rows: { ForEach(items) { item in TableRow(item) .frame(height: 44) // 设置你需要的固定行高 .tableRowInsets(EdgeInsets()) // 移除默认内边距 } }
2. 缓存筛选结果,减少重复计算
当前视图中的items计算属性每次筛选都会遍历全量数据,数据量大时会加重性能负担。可以在AppModel中缓存筛选后的结果:
// 在AppModel中添加 var filteredItems: [Item] = [] func updateFilteredItems() { filteredItems = items.filter { selectedFilters.contains($0.name) } }
然后在视图中监听筛选条件变化,触发更新:
// 在ContentView的body中添加 .onChange(of: appModel.selectedFilters) { appModel.updateFilteredItems() }
之后直接使用appModel.filteredItems作为表格的数据源,替代原来的计算属性。
3. 移除不必要的全局动画
当前.animation(.smooth, value: items.count)会对整个NavigationSplitView应用动画,这会额外增加渲染负担。如果不需要这个动画可以直接移除;如果需要局部动画,仅给侧边栏或表格添加针对性动画即可。
4. 固定表格列的内容布局
对于使用value:初始化的表格列,设置内容不换行,避免表格因内容变化重新计算行高:
TableColumn("c1", value: \.c1) .fixedSize(horizontal: true, vertical: false)
内容的提问来源于stack exchange,提问作者Nikolai Nagornyi
相关产品推荐
相关产品推荐

