SwiftUI中不使用系统NavigationView为List添加搜索栏的方案
完全可以实现,不需要依赖系统内置NavigationView/NavigationStack的搜索能力,手动搭建搜索栏+数据过滤逻辑即可,和你已有的自定义导航栏可以无缝适配。
具体实现步骤
自定义独立搜索栏组件
搜索栏本身就是普通的SwiftUI视图组合,不需要依附任何系统导航容器,核心是通过@Binding对外暴露搜索输入状态,内置输入框、搜索图标、清除按钮、取消按钮基础交互即可,示例代码:import SwiftUI struct CustomSearchBar: View { @Binding var searchText: String @State private var isEditing = false var body: some View { HStack(spacing: 8) { Image(systemName: "magnifyingglass") .foregroundColor(.secondary) TextField("输入关键词搜索", text: $searchText) .padding(7) .padding(.horizontal, 24) .background(Color(.systemGray6)) .cornerRadius(8) .overlay( HStack { Spacer() if isEditing, !searchText.isEmpty { Button { searchText = "" } label: { Image(systemName: "xmark.circle.fill") .foregroundColor(.secondary) .padding(.trailing, 8) } } } ) .onTapGesture { withAnimation { isEditing = true } } if isEditing { Button("取消") { withAnimation { isEditing = false searchText = "" UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil) } } .foregroundColor(.accentColor) .transition(.move(edge: .trailing)) } } .padding(.horizontal, 16) } }整合自定义导航栏、搜索栏与List组件
将自定义搜索栏放在你已完成的自定义导航栏下方(或导航栏内部,根据你的设计需求调整位置),在业务视图中维护搜索关键词状态,基于关键词对列表原始数据做过滤,将过滤后的数据传给List渲染即可。注意:系统提供的
.searchable()修饰符必须依附系统导航容器才能生效,脱离NavigationView/NavigationStack使用不会展示任何搜索UI,不要用这个修饰符。整合示例代码:
struct ListPage: View { // 原始列表数据源 @State private var originList = ["SwiftUI", "UIKit", "Objective-C", "Flutter", "React Native", "HarmonyOS"] // 搜索关键词状态 @State private var searchText = "" // 过滤后的展示数据 var displayList: [String] { let keyword = searchText.trimmingCharacters(in: .whitespacesAndNewlines) guard !keyword.isEmpty else { return originList } return originList.filter { $0.localizedCaseInsensitiveContains(keyword) } } var body: some View { VStack(spacing: 0) { // 你已经搭建完成的自定义导航栏 CustomNavigationBar(title: "内容列表") // 新增的自定义搜索栏 CustomSearchBar(searchText: $searchText) .padding(.vertical, 8) // 列表组件,直接使用过滤后的数据 List(displayList, id: \.self) { item in Text(item) .padding(.vertical, 4) } .listStyle(.plain) } } }可选扩展优化
- 滚动收起键盘:iOS16及以上系统可以直接给
List加.scrollDismissesKeyboard(.immediately)修饰符,低版本可以给List加滑动手势监听,滑动时主动调用收起键盘的方法即可 - 输入防抖:如果列表数据量较大、或者搜索逻辑需要触发网络请求,可以引入Combine框架对搜索输入做防抖处理,避免频繁触发过滤/请求逻辑,核心代码:
import Combine // 在业务视图中添加持有订阅的集合 @State private var cancellableSet = Set<AnyCancellable>() // 在视图的onAppear修饰符中绑定防抖逻辑 .onAppear { $searchText .debounce(for: .milliseconds(300), scheduler: DispatchQueue.main) .sink { keyword in // 这里可以执行搜索请求、重排数据等逻辑 } .store(in: &cancellableSet) } - 样式适配:搜索栏的颜色、圆角、边距、图标都可以根据你App的设计规范自由调整,不会出现系统搜索栏和自定义导航栏风格不统一的问题。
- 滚动收起键盘:iOS16及以上系统可以直接给
内容的提问来源于stack exchange,提问作者Mc.Lover
相关产品推荐
相关产品推荐

