SwiftUI结合CoreData实现2000条列表搜索过滤性能优化咨询
列表搜索性能优化方案
1. 移除导致列表全量重绘的错误代码
你当前代码中给List加了.id(UUID())配置,这会导致每次搜索文本变化、视图刷新时,整个列表被完全销毁重建,不会复用任何已渲染的单元格,是卡顿的核心原因之一,直接删除这行代码即可。
2. 将内存过滤改为CoreData层查询,利用数据库的查询优化能力
当前你是先把2000条数据全部从数据库加载到内存,再在SwiftUI渲染时做遍历过滤,效率极低。可以直接给@FetchRequest加动态NSPredicate,让过滤逻辑在SQL层面执行,性能会提升数倍:
- 提前在Song实体中新增两个预处理存储字段:
normalizedContent:写入Song数据时,提前把content转成小写、移除数字后存储,避免每次搜索都重复计算numberStr:提前把number转成字符串存储,避免搜索时重复转换
- 给这两个字段在CoreData模型编辑器中开启索引,进一步提升查询速度
3. 优化搜索触发逻辑,添加防抖
用户连续输入时不需要每输入一个字符就触发一次过滤,添加300ms左右的防抖,只有输入停顿后才执行查询,减少无效计算。
修改后参考代码
import CoreData import SwiftUI import Combine struct SongbookView: View { @Environment(\.managedObjectContext) var managedObjectContext @FetchRequest( entity: Song.entity(), sortDescriptors: [NSSortDescriptor(keyPath: \Song.number, ascending: true)] ) var songs: FetchedResults<Song> @State private var searchText = "" // 防抖用的最终查询文本 @State private var queryText = "" private var cancelBag = Set<AnyCancellable>() init() { // 绑定搜索文本防抖,300ms无输入后触发查询 $searchText .debounce(for: .milliseconds(300), scheduler: DispatchQueue.main) .assign(to: \.queryText, on: self) .store(in: &cancelBag) } var body: some View { NavigationView{ VStack{ SearchBar(text: $searchText) Spacer() List(songs, id:\.objectID) { song in NavigationLink(destination: DetailView(song: song, isSelected: song.favorite)) { HStack{ Text("\(String(song.number)). ") .font(.headline) + Text(song.title ?? "Brak tytułu") if song.favorite { Spacer() Image(systemName: "heart.fill") .accessibility(label: Text("To jest ulubiona pieśń")) .foregroundColor(.red) } }.lineLimit(1) } } // 已删除导致全量重绘的.id(UUID())配置 } .listStyle(InsetListStyle()) .navigationTitle("Śpiewnik") .onChange(of: queryText) { newValue in // 动态更新FetchRequest的查询谓词 if newValue.isEmpty { songs.nsPredicate = nil } else { let lowerQuery = newValue.lowercased() songs.nsPredicate = NSPredicate(format: "normalizedContent CONTAINS %@ OR numberStr CONTAINS %@", lowerQuery, lowerQuery) } } } } } extension UINavigationController { // Remove back button text open override func viewWillLayoutSubviews() { navigationBar.topItem?.backBarButtonItem = UIBarButtonItem(title: "", style: .plain, target: nil, action: nil) } }
额外可选优化
如果后续数据量继续增长,可以新增分页加载逻辑,首次只加载前100条,滚动到底部再加载更多;同时可以在FetchRequest里设置propertiesToFetch,只查询列表渲染需要的number、title、favorite三个字段,减少数据加载开销。
内容的提问来源于stack exchange,提问作者oculorum
相关产品推荐
相关产品推荐

