如何在嵌套数组结构的UITableView中实现Sign内容搜索过滤?
问题描述
我定义了两个类:
class Sign { let code: String let name: String let description: String let picture: String init(code: String, name: String, description: String, picture: String) { self.code = code self.name = name self.description = description self.picture = picture } } class Category { let name: String let sign: [Sign] init(name: String, sign: [Sign]) { self.name = name self.sign = sign } }
在UITableView中,我将Category作为分组标题(section title),Sign作为分组行(section row)展示。现在想在TableView上方添加SearchBar实现搜索功能,但目前仅能对Category名称进行过滤。我需要实现:当在搜索栏输入关键词(如"yy"或"y")时,TableView仅显示名称包含该关键词的Sign,同时保留其所属的Category作为分组标题。
示例:
var categories: [Category] = [ Category(name: "X", sign: [Sign(code: "X-1", name: "***")]), Category(name: "Y", sign: [Sign(code: "Y-1", name: "Yyy")]) ]
输入"yy"后,需仅显示名称含"yy"的Sign条目及其所属Category。
当前TableView配置代码:
override func numberOfSections(in tableView: UITableView) -> Int { return filteredCategories.count } override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return filteredCategories[section].sign.count } override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { switch section { case 0: return filteredCategories[0].name case 1: return filteredCategories[1].name case 2: return filteredCategories[2].name case 3: return filteredCategories[3].name default: return "Error" } } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: PropertyKeys.categorieCell, for: indexPath) as! SignTableViewCell let categorie = filteredCategories[indexPath.section] let sign = categorie.sign[indexPath.row] cell.signImagemin.image = UIImage(named: sign.picture) cell.signCodeMin.text = sign.code cell.signDescriptionMin.text = sign.name return cell }
当前SearchBar实现代码:
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) { filteredCategories = [] let text = searchText.lowercased() if searchText == "" { filteredCategories = categories } else { for categorie in categories { if categorie.name.lowercased().contains(searchText.lowercased()) { let signs = categorie.sign.filter() { $0.name.contains(searchText) } let newCat = categorie newCat.sign = signs filteredCategories.append(newCat) } } } self.tableView.reloadData() }
需求效果:原本TableView展示完整的分组和行,输入关键词后,仅显示包含该关键词的Sign及其所属的Category分组标题。
解决方案
1. 修改Category类的属性
原Category类中sign是let常量,无法修改,需要改成var变量,这样才能创建过滤后的Sign数组并赋值:
class Category { let name: String var sign: [Sign] // 将let改为var init(name: String, sign: [Sign]) { self.name = name self.sign = sign } }
2. 修改搜索逻辑
当前代码只过滤了名称包含关键词的Category,现在需要改为:遍历每个Category,过滤出该Category下名称包含关键词的Sign;如果过滤后的Sign数组不为空,就创建一个新的Category实例(基于原Category,替换为过滤后的Sign数组),添加到filteredCategories中:
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) { filteredCategories = [] let lowercasedText = searchText.lowercased() if searchText.isEmpty { filteredCategories = categories } else { for category in categories { // 过滤当前Category下名称包含关键词的Sign(忽略大小写) let filteredSigns = category.sign.filter { $0.name.lowercased().contains(lowercasedText) } // 仅当过滤后有Sign时,才添加该Category到结果集合 if !filteredSigns.isEmpty { let filteredCategory = Category(name: category.name, sign: filteredSigns) filteredCategories.append(filteredCategory) } } } tableView.reloadData() }
3. 优化TableView分组标题方法
原titleForHeaderInSection用switch判断section,扩展性差,直接通过section索引获取对应Category名称即可:
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { guard section < filteredCategories.count else { return "Error" } return filteredCategories[section].name }
关键说明
- 搜索时忽略大小写,提升关键词匹配的友好度;
- 避免直接修改原Category的Sign数组,通过创建新实例的方式保证原始数据不被污染;
- 只有包含符合条件Sign的Category才会出现在过滤结果中,确保界面仅展示有效内容。
内容的提问来源于stack exchange,提问作者krzychug
相关产品推荐
相关产品推荐

