macOS Big Sur下SwiftUI实现搜索栏过滤列表NoteItem模型text字段
解决方案
疑问统一解答
- 你代码里的
$searchText就是搜索框输入内容的绑定,直接使用searchText即可拿到当前输入的文本值 - 不需要额外创建存储型数组存过滤结果,用计算属性即可,每次搜索词或全量笔记变化时会自动重新计算,永远基于完整的
data.notes做过滤,不会出现只过滤当前列表的问题 - 过滤直接操作全量的
data.notes数组即可,不需要修改DataModel的原有逻辑
修复布局错误(解决NavigationLink前随机空白)
你当前的代码把listStyle、frame、alert修饰符,还有右侧占位的Text("Create a new note...")都写到了List的内容闭包里,这些元素会被系统当成列表项渲染,才会出现随机空白、多余行的问题。正确的NavigationView双栏结构是列表和右侧占位视图平级,修饰符要加在对应元素的括号外。
完整实现代码
struct AllNotes: View { @EnvironmentObject private var data: DataModel // 确认已经声明搜索词状态 @State private var searchText = "" @State private var selectedNoteId: UUID? @State private var showAlert = false // 你自己的alert定义 private var alert: Alert { Alert(title: Text("提示")) } // 过滤后的笔记计算属性,永远基于全量data.notes计算 private var filteredNotes: [NoteItem] { if searchText.isEmpty { return data.notes } else { // 这里用localizedCaseInsensitiveContains实现不区分大小写搜索,不需要可以换回contains return data.notes.filter { $0.text.localizedCaseInsensitiveContains(searchText) } } } // 提取获取标题的方法,你自己的原有逻辑 private func getTitle(noteText: String) -> String { noteText.components(separatedBy: .newlines).first ?? "无标题" } var body: some View { NavigationView { // 左侧笔记列表 List { ForEach(filteredNotes) { note in NavigationLink( destination: NoteView(note: note, text: note.text), tag: note.id, selection: $selectedNoteId ) { VStack(alignment: .leading) { Text(getTitle(noteText: note.text)).font(.body).fontWeight(.bold) Text(note.dateText).font(.body).fontWeight(.light) } .padding(.vertical, 10) } } } // 所有List的修饰符要放在List括号外面,不要放在List的内容闭包里 .listStyle(InsetListStyle()) .frame(minWidth: 250, maxWidth: .infinity) .alert(isPresented: $showAlert, content: { alert }) // 右侧无选中时的占位视图,和List平级,不要放在List里面 Text("Create a new note...") .frame(maxWidth: .infinity, maxHeight: .infinity) } .toolbar { ToolbarItem(placement: .automatic) { TextField("Search...", text: $searchText) .textFieldStyle(RoundedBorderTextFieldStyle()) .frame(minWidth: 200) } } } }
可选优化
如果搜索的笔记量很大,可以加300ms左右的防抖逻辑,避免每次输入都立刻过滤,减少性能消耗:
import Combine // 在AllNotes里加声明 @State private var debouncedSearchText = "" private let searchPublisher = PassthroughSubject<String, Never>() private var debounceCancellable: AnyCancellable? // 在init里加防抖逻辑 init() { debounceCancellable = searchPublisher .debounce(for: .milliseconds(300), scheduler: DispatchQueue.main) .assign(to: \.debouncedSearchText, on: self) } // 搜索框绑定修改为 TextField("Search...", text: $searchText) .onChange(of: searchText) { newValue in searchPublisher.send(newValue) } // 过滤逻辑里的searchText改为debouncedSearchText
内容的提问来源于stack exchange,提问作者Aleph
相关产品推荐
相关产品推荐

