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

为何SwiftData的@Query会忽略搜索关键词?

问题:SwiftData @Query 搜索过滤不生效的原因?

我写了下面的SwiftUI + SwiftData代码,想用@Query结合搜索文本过滤Item列表,但输入关键词后items数组完全没变化;换成在内存中过滤的写法却能正常工作。

不生效的代码:

import SwiftUI
import SwiftData

@Model
final class Item {
    @Attribute(.unique) var id = UUID()
    private(set) var name: String
    
    init(name: String) {
        self.name = name
    }
}

struct ContentView: View {
    
    @State private var searchText: String
    
    @Query private var items: [Item]
    
    init(searchText: String = "") {
        _searchText = State(initialValue: searchText)
        _items = Query(filter: #Predicate<Item> {
            if searchText.isEmpty {
                return true
            } else {
                return $0.name.localizedStandardContains(searchText)
            }
        }, sort: [SortDescriptor(\Item.name)])
    }
    
    var body: some View {
        NavigationStack {
            List {
                Section("Items") {
                    ForEach(items) { item in
                        Text(item.name)
                    }
                }
            }.searchable(text: $searchText)
        }
    }
}

生效的内存过滤写法:

var filteredItems: [Item] {
    guard !searchText.isEmpty else { return items }
    return items.filter {
        $0.name.localizedStandardContains(searchText)
    }
} 

请问我遗漏了什么关键内容?


原因分析

不生效的核心问题

你在初始化里创建的@Query是静态的——它只会用初始化时的searchText初始值生成过滤条件,之后searchText变化时,这个Query的过滤规则不会自动更新。@Query的初始化参数不会自动响应State变量的变化,相当于把过滤条件“固定”在了初始状态。

为什么内存过滤写法生效

filteredItems是一个计算属性,每次searchText改变时,SwiftUI都会重新计算这个属性的值,自然能实时过滤内存中的items数组,所以能正常响应搜索输入。

正确的@Query动态过滤写法

要让@Query响应searchText变化,需要在searchText更新时重新创建Query,比如用onChange监听变化:

struct ContentView: View {
    @State private var searchText: String = ""
    @Query private var items: [Item]
    
    init() {
        // 初始化时先设置默认排序
        _items = Query(sort: [SortDescriptor(\Item.name)])
    }
    
    var body: some View {
        NavigationStack {
            List {
                Section("Items") {
                    ForEach(items) { item in
                        Text(item.name)
                    }
                }
            }
            .searchable(text: $searchText)
            // 监听searchText变化,更新Query的过滤条件
            .onChange(of: searchText) { _, newText in
                _items = Query(filter: #Predicate<Item> {
                    newText.isEmpty ? true : $0.name.localizedStandardContains(newText)
                }, sort: [SortDescriptor(\Item.name)])
            }
        }
    }
}

或者也可以用动态生成FetchDescriptor的方式,直接通过ModelContext获取过滤后的结果:

struct ContentView: View {
    @State private var searchText: String = ""
    @Environment(\.modelContext) private var modelContext
    
    private var items: [Item] {
        let predicate = searchText.isEmpty 
            ? #Predicate<Item> { _ in true } 
            : #Predicate<Item> { $0.name.localizedStandardContains(searchText) }
        let descriptor = FetchDescriptor<Item>(predicate: predicate, sortBy: [SortDescriptor(\Item.name)])
        return (try? modelContext.fetch(descriptor)) ?? []
    }
    
    var body: some View {
        NavigationStack {
            List {
                Section("Items") {
                    ForEach(items) { item in
                        Text(item.name)
                    }
                }
            }
            .searchable(text: $searchText)
        }
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.12 15:17:17