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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 20:15:06