如何实现Apple Reminders同款搜索图标布局与交互效果?
实现Apple Reminders风格的搜索功能(SwiftUI)
核心实现思路
要完全匹配Reminders的搜索交互,需要借助UIKit原生组件的特性来修正SwiftUI默认搜索栏的不足,核心解决方向:
- 用
UISearchController的原生行为让搜索框附着在键盘顶部 - 自定义搜索栏布局,替换默认内部关闭按钮为独立外部按钮
- 通过状态绑定实现点击关闭按钮或收起键盘时一步隐藏搜索框
完整代码实现
import SwiftUI import UIKit // 包装UIKit搜索控制器,适配SwiftUI struct ReminderSearchController: UIViewControllerRepresentable { @Binding var isSearchActive: Bool @Binding var searchText: String let onCancel: () -> Void func makeUIViewController(context: Context) -> UISearchController { let searchController = UISearchController(searchResultsController: nil) searchController.searchBar.delegate = context.coordinator searchController.hidesNavigationBarDuringPresentation = false searchController.obscuresBackgroundDuringPresentation = false // 隐藏默认内部取消按钮,添加独立外部按钮 let cancelBtn = UIButton(type: .system) cancelBtn.setTitle("取消", for: .normal) cancelBtn.addTarget(context.coordinator, action: #selector(Coordinator.handleCancel), for: .touchUpInside) searchController.searchBar.showsCancelButton = false searchController.searchBar.addSubview(cancelBtn) // 布局取消按钮到搜索栏右侧 cancelBtn.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ cancelBtn.centerYAnchor.constraint(equalTo: searchController.searchBar.centerYAnchor), cancelBtn.trailingAnchor.constraint(equalTo: searchController.searchBar.trailingAnchor, constant: -16) ]) return searchController } func updateUIViewController(_ uiViewController: UISearchController, context: Context) { uiViewController.isActive = isSearchActive uiViewController.searchBar.text = searchText } func makeCoordinator() -> Coordinator { Coordinator(parent: self) } class Coordinator: NSObject, UISearchBarDelegate { let parent: ReminderSearchController init(parent: ReminderSearchController) { self.parent = parent } @objc func handleCancel() { parent.searchText = "" parent.isSearchActive = false parent.onCancel() } func searchBarTextDidEndEditing(_ searchBar: UISearchBar) { // 收起键盘时自动关闭搜索框 parent.isSearchActive = false } } } // 主界面视图 struct ReminderMainView: View { @State private var isSearchActive = false @State private var searchText = "" @State private var reminderItems = ["买菜", "整理桌面", "完成代码优化"] var body: some View { NavigationStack { List(filteredItems) { item in Text(item) } .navigationTitle("提醒事项") .navigationBarItems( leading: Button(action: { /* 设置按钮逻辑 */ }) { Image(systemName: "gear") }, trailing: Button(action: { isSearchActive = true }) { Image(systemName: "magnifyingglass") } ) // 嵌入自定义搜索控制器 .overlay( ReminderSearchController( isSearchActive: $isSearchActive, searchText: $searchText, onCancel: { /* 取消搜索后的额外逻辑 */ } ) .frame(height: 0) // 隐藏默认占位高度 ) .toolbar { ToolbarItem(placement: .bottomBar) { Button(action: { /* 添加新提醒逻辑 */ }) { Image(systemName: "plus.circle.fill") .font(.title) } } } } } // 搜索过滤逻辑 private var filteredItems: [String] { guard !searchText.isEmpty else { return reminderItems } return reminderItems.filter { $0.localizedCaseInsensitiveContains(searchText) } } } #Preview { ReminderMainView() }
问题针对性解决说明
- 搜索框附着键盘顶部:借助
UISearchController的原生交互,它会自动在激活时附着于键盘上方,替代SwiftUI默认的顶部搜索栏布局。 - 独立关闭按钮:隐藏搜索栏默认的内部取消按钮,手动添加独立的
UIButton并布局到搜索栏右侧,点击后直接触发关闭逻辑。 - 一步关闭操作:通过
isSearchActive状态绑定,点击关闭按钮时同时清空搜索文本并设置isSearchActive = false;同时监听搜索栏结束编辑事件,收起键盘时自动关闭搜索框,实现一步完成关闭。
额外优化建议
- 可以给搜索栏添加圆角、阴影,更贴近Reminders的视觉风格
- 搜索过滤逻辑可添加防抖处理,避免实时输入频繁触发过滤操作
内容的提问来源于stack exchange,提问作者Edward
相关产品推荐
相关产品推荐

