You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.05 14:09:03