UISearchController索引越界问题及WhatsApp群成员选择功能实现求助
我来帮你一步步解决这个问题,咱们先拆解核心问题和需求,逐个击破:
一、解决搜索时的Index Out Of Range崩溃
首先分析崩溃根源:你在didSelectRowAt里用了selectedUsers.index(of: cell.user)!的强制解包,如果因为状态不同步(比如重复点击、搜索状态下数据刷新)导致cell.user不在selectedUsers里,直接就会触发崩溃。另外,cell.user的可选性也可能带来隐患,咱们调整代码:
修复代码
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { guard let cell = tableView.cellForRow(at: indexPath) as? GroupRegistrationTableViewCell, let selectedUser = cell.user else { return } cell.added = !cell.added if cell.added { addRecipient(user: selectedUser) } else { // 安全获取索引,避免强制解包崩溃 if let removeIndex = selectedUsers.index(of: selectedUser) { deleteRecipient(user: selectedUser, index: removeIndex) } } } // 同时优化cellForRowAt,直接用明确的user对象判断选中状态 override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "GroupRegistrationTableViewCell") as! GroupRegistrationTableViewCell let targetUser: User if isFiltering() { targetUser = filteredUsers[indexPath.row] } else { targetUser = usersArray[indexPath.row] } cell.user = targetUser cell.added = selectedUsers.contains(targetUser) // 直接用确定的user对象判断,避免cell.user的可选问题 return cell }
二、确保selectedUsers数组无重复
当前addRecipient方法直接append用户,没有去重判断,会导致数组里出现重复项。咱们修改这个方法:
func addRecipient(user: User) { // 添加前先判断是否已存在,避免重复 guard !selectedUsers.contains(user) else { return } selectedUsers.append(user) }
三、彻底移除搜索栏的取消按钮
你现在在updateSearchResults里设置取消按钮隐藏,但第一次点击搜索栏时系统会默认显示,咱们换个更彻底的方式:
override func viewDidLoad() { super.viewDidLoad() self.fetchUsers() navigationItem.title = "GroupRegistration" navigationItem.backBarButtonItem = UIBarButtonItem(title: "", style: .plain, target: nil, action: nil) searchController.searchResultsUpdater = self searchController.dimsBackgroundDuringPresentation = false definesPresentationContext = true tableView.tableHeaderView = searchController.searchBar searchController.searchBar.placeholder = "Search Members" searchController.hidesNavigationBarDuringPresentation = false // 关键:一开始就隐藏取消按钮 searchController.searchBar.showsCancelButton = false // 监听搜索栏开始编辑事件,确保取消按钮不会弹出来 searchController.searchBar.addTarget(self, action: #selector(searchBarDidBeginEditing), for: .editingDidBegin) } @objc private func searchBarDidBeginEditing() { searchController.searchBar.showsCancelButton = false }
额外优化建议
- 异步加载用户的线程安全:
Firebase的回调是在后台线程,更新UI必须切回主线程,同时搜索状态下要同步过滤结果:
func fetchUsers() { DatabaseReference.users(uid: currentUser.userUID).reference().child("follows").observe(.childAdded, with: { (snapshot) in DispatchQueue.main.async { let user = User(dictionary: snapshot.value as! [String:Any]) self.usersArray.insert(user, at: 0) let indexPath = IndexPath(row: 0, section: 0) self.tableView.insertRows(at: [indexPath], with: .fade) // 如果当前在搜索状态,同步更新过滤列表 if self.isFiltering() { self.filteredContentForSearchText(searchText: self.searchController.searchBar.text) } } }) }
- 优化搜索过滤方法:
避免强制解包搜索文本,让代码更健壮:
func filteredContentForSearchText(searchText: String?, scope: String = "All") { guard let text = searchText, !text.isEmpty else { filteredUsers = usersArray tableView.reloadData() return } filteredUsers = usersArray.filter { $0.fullName.lowercased().contains(text.lowercased()) } tableView.reloadData() } func updateSearchResults(for searchController: UISearchController) { filteredContentForSearchText(searchText: searchController.searchBar.text) searchController.searchBar.showsCancelButton = false }
这样修改后,你的三个需求都能完美满足,同时彻底解决搜索时的崩溃问题。
内容的提问来源于stack exchange,提问作者bibscy
相关产品推荐
相关产品推荐

