SwiftUI中层级菜单搜索过滤不全的技术求助
SwiftUI 多层级菜单搜索过滤问题
问题背景
在SwiftUI中实现菜单搜索功能时,无法正确遍历所有层级菜单并过滤出匹配关键词的项。当前代码仅能返回部分结果,需要实现**任意层级的title匹配关键词时,将该菜单加入filteredModuel**的功能。
API响应结构
{"errorCode": 0,"status": "ok","message": "App Layout","menus": [{"menuID": 1,"icon": "https://api/AppIcons/svg/Students.svg","title": "Students","childMenus": [{"chMenuID": 1,"menuID": 0,"title": "Student Profile","icon": "https://api/AppIcons/svg/Students.svg","childMenus": null},{"chMenuID": 41,"menuID": 0,"title": "Update Record","icon": "https://api/AppIcons/svg/Students.svg","childMenus": [{"sbChMenuID": 1,"chMenuID": 0,"title": "Class Promotion","icon": "https://api/AppIcons/svg/Students.svg"}]}]},{"menuID": 2,"title": "Staff","icon": "https://api/AppIcons/svg/Students.svg","childMenus": [{"chMenuID": 4,"menuID": 0,"title": "Staff Profile","icon": "https://api/AppIcons/svg/Students.svg","childMenus": null},{"chMenuID": 6,"menuID": 0,"title": "Staff Leave","icon": "https://api/AppIcons/svg/Students.svg","childMenus": null},{"chMenuID": 62,"menuID": 0,"title": "Staff Attendance","icon": "https://api/AppIcons/svg/Students.svg","childMenus": null}]}]}
需求说明
- 搜索关键词
staf时,期望返回:[Staff Profile、Staff Leave、Staff Attendance] - 搜索关键词
profil时,期望返回:[Staff Profile、Student Profile]
当前代码
SearchView 实现
struct SearchView: View { @State private var searchKeyword: String = "" @State private var filteredModuel: [AppMenu] = [] @State private var selectedIndex: Int? = 4 @State private var navigateModule: Bool = false @State private var moduleId: String = "" var body: some View { VStack(spacing: 0) { // 在TabView中展示filterDataModuel()的结果 filterDataModuel() // 其他代码 } } private func filterDataModuel() { let lowercasedKeyword = searchKeyword.lowercased() filteredModuel = [] if let menus = viewModelSidemenu.layout?.menus { for menu in menus { let filteredChildMenus = menu.childMenus?.filter { childMenu in let childTitleMatches = childMenu.title?.lowercased().contains(lowercasedKeyword) ?? false return childTitleMatches } ?? [] let titleMatches = menu.title?.lowercased().contains(lowercasedKeyword) ?? false if !filteredChildMenus.isEmpty { filteredModuel.append(contentsOf: filteredChildMenus) } if titleMatches && filteredChildMenus.isEmpty { filteredModuel.append(menu) } } } } @ViewBuilder func moduelView(moduel: AppMenu) -> some View { VStack { let urlStr = moduel.icon ?? "" URLImageView(url: urlStr, placeholder: "NoProfilePic", width: 100, height: 100, renderingMode: .template) .foregroundStyle(.black) .padding() Text(moduel.title ?? " ") .font(.calibriBold(with: 16)) .foregroundStyle(Color.hex1E1D0E) .multilineTextAlignment(.center) } } }
数据模型
// MARK: - AppLayoutModel struct AppLayoutModel: Codable { let status: String? let isAuthenticated: Bool? let menus: [AppMenu]? let errorCode: Int? enum CodingKeys: String, CodingKey { case status, isAuthenticated, menus, userInfo, errorCode } } struct AppMenu: Codable, Identifiable { let title, icon: String? let menuID, chMenuID: Int? let slNo: Int? var childMenus: [AppMenu]? var uniqueID: Int { return menuID ?? -1 } var id: Int { return uniqueID } }
解决方案
当前代码仅遍历了顶层菜单和第一层子菜单,未处理更深层级的子菜单,且逻辑存在匹配规则问题。通过递归遍历所有层级菜单的方式,可以实现任意层级的匹配收集。
修改filterDataModuel函数如下:
private func filterDataModuel() { let lowercasedKeyword = searchKeyword.lowercased() filteredModuel = [] guard let menus = viewModelSidemenu.layout?.menus else { return } // 递归遍历所有层级,收集匹配的菜单 func collectMatchingMenus(from menus: [AppMenu]) -> [AppMenu] { var matchedMenus: [AppMenu] = [] for menu in menus { // 检查当前菜单标题是否匹配关键词 if let title = menu.title, title.lowercased().contains(lowercasedKeyword) { matchedMenus.append(menu) } // 递归处理子菜单,收集子菜单中的匹配项 if let childMenus = menu.childMenus { matchedMenus.append(contentsOf: collectMatchingMenus(from: childMenus)) } } return matchedMenus } filteredModuel = collectMatchingMenus(from: menus) }
代码说明
- 定义递归函数
collectMatchingMenus,负责遍历传入的菜单数组:- 先检查当前菜单的
title是否匹配关键词,匹配则加入结果数组 - 若当前菜单存在子菜单,递归调用自身处理子菜单,并将子菜单的匹配项追加到结果数组
- 先检查当前菜单的
- 顶层调用该递归函数,传入所有顶层菜单,最终将所有层级的匹配项赋值给
filteredModuel
此方案可以覆盖所有层级的菜单,完全满足需求中的搜索结果要求。
内容的提问来源于stack exchange,提问作者Swift
相关产品推荐
相关产品推荐

