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

SwiftUI Core Data歌单列表如何实现自定义预处理+Combine防抖搜索优化

实现方案

改动说明

  • 引入Combine框架实现输入防抖,默认设置0.3秒延迟,可根据需求调整时长
  • 完全保留原有自定义文本清洗、小写对比逻辑,不使用NSPredicate
  • 新增防抖后搜索词状态、预计算过滤歌曲数组,减少实时计算开销
  • 空搜索词直接触发结果更新,无需等待防抖延迟,提升交互体验

完整修改后代码

歌单列表代码(仅需修改这个文件,搜索栏代码无需改动)

import CoreData
import SwiftUI
import Combine // 新增导入Combine框架

struct SongbookView: View {
    @State var searchText: String = ""
    @State private var debouncedSearchText: String = ""
    @State private var filteredSongs: [Song] = []
    @State private var searchCancellable: AnyCancellable? // 持有Combine订阅
    
    @Environment(\.managedObjectContext) var managedObjectContext
    @FetchRequest(
        entity: Song.entity(),
        sortDescriptors: [NSSortDescriptor(keyPath: \Song.number, ascending: true)]
    ) var songs: FetchedResults<Song>
    
    var body: some View {
        NavigationView{
            VStack{
                SearchBar(text: $searchText)
                Spacer()
                // 直接使用预过滤的数组,避免实时计算
                List(filteredSongs, 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())
                .animation(.default)
            }
            .padding(.top, 10)
            .navigationBarTitleDisplayMode(.inline)
            .toolbar {
                ToolbarItem(placement: .principal) {
                    Text("Śpiewnik")
                        .font(.system(size: 20))
                        .bold()
                }
            }
            .onAppear {
                setupSearchPipeline()
                updateFilteredSongs(searchText: "")
            }
            // Core Data数据变更时自动更新过滤结果
            .onChange(of: songs) { _ in
                updateFilteredSongs(searchText: debouncedSearchText)
            }
        }
    }
    
    // 配置Combine搜索防抖管道
    private func setupSearchPipeline() {
        searchCancellable = $searchText
            .debounce(for: .seconds(0.3), scheduler: DispatchQueue.main) // 防抖延迟,可自行调整时长
            .removeDuplicates() // 过滤重复输入,避免无效搜索
            .sink(receiveValue: { [weak self] text in
                self?.updateFilteredSongs(searchText: text)
            })
    }
    
    // 统一搜索过滤逻辑,保留原有自定义清洗规则
    private func updateFilteredSongs(searchText: String) {
        debouncedSearchText = searchText
        guard !searchText.isEmpty else {
            filteredSongs = Array(songs)
            return
        }
        let lowercasedQuery = searchText.lowercased()
        filteredSongs = songs.filter { song in
            let cleanedContent = removeNumber(str: song.content?.lowercased() ?? "")
            return cleanedContent.contains(lowercasedQuery) || String(song.number).contains(lowercasedQuery)
        }
    }
    
    func removeNumber(str: String) -> String {
        var result = str
        let vowels: Set<Character> = ["1", "2", "3", "4", "5", "6", "7", "8", "9"]
        result.removeAll(where: { vowels.contains($0) })
        return result
    }
}

内容的提问来源于stack exchange,提问作者oculorum

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.03 15:45:04