SwiftUI列表分类去重及'[AnyHashable:Any]无category成员'报错解决
问题1:
[AnyHashable : Any] 无 category 成员报错 报错由三个问题共同导致:
- 用
[String: Any]类型的字典数组存游戏机数据,属于弱类型存储,编译器无法确认字典里存在category字段,自然不支持点语法访问 - 结构体内部属性初始化顺序错误,定义
categories时consoles属性还未完成初始化,编译器无法识别该属性 - 全局定义的空字符串
imgName、category等作为字典key属于冗余代码,无实际作用还容易引发类型推断错误。
解决方案是先定义强类型的Console模型替代字典存储,同时调整结构体内部属性的初始化顺序:
import Foundation // 强类型游戏机模型,替代原有的字典存储方式 struct Console: Identifiable { let id = UUID() let imgName: String let consoleName: String let description: String let initialPrice: Double let releaseDate: String let discontinuation: Int let category: String } // 结构体名遵循大驼峰命名规范 struct ConsoleList { // 先初始化数据源,再做分组逻辑 let consoles: [Console] = [ Console(imgName: "FAMICOM", consoleName: "Famicom", description: "It was released in 1983 in Japan and due to it success it gave birth to the NES", initialPrice: 179, releaseDate: "Release Date: July 15, 1983", discontinuation: 2003, category: "Home Consoles"), Console(imgName: "NES", consoleName: "NES", description: "It was released in 1985 in the US and EU and was the european version of the Famicom", initialPrice: 179, releaseDate: "Release Date: Oct 18, 1985", discontinuation: 1991, category: "Home Consoles"), Console(imgName: "US_SNES", consoleName: "SNES, Super Famicom", description: "Nintendo released the Super Nintendo Entertainment System, a redesigned version of the Super Famicom, in North America for US$199 (equivalent to $400 in 2021).", initialPrice: 199, releaseDate: "Release Date: Sept 9, 1991", discontinuation: 1999, category: "Home Consoles"), Console(imgName: "GB_FAT", consoleName: "Original GameBoy", description: "The Game Boy is the first handheld in the Game Boy family, it was first released in Japan on April 21, 1989.", initialPrice: 89.99, releaseDate: "Release Date: Apr 21, 1989", discontinuation: 2003, // 修正原代码中200的笔误 category: "Handheld Consoles"), Console(imgName: "GB_POCKET", consoleName: "GameBoy Pocket", description: "On July 21, 1996, Nintendo released the Game Boy Pocket for US$69.99: a smaller, lighter unit that required fewer batteries. It has space for two AAA batteries, which provide approximately 10 hours of gameplay.", initialPrice: 69.99, releaseDate: "Release Date: Jul 21, 1996", discontinuation: 2003, category: "Handheld Consoles"), Console(imgName: "GB_LIGHT", consoleName: "GameBoy Light", description: "The Game Boy Light is the second and final revision of the original Game Boy exclusive to Japan.", initialPrice: 89.99, releaseDate: "Release Date: Apr 14, 1998", discontinuation: 2003, category: "Handheld Consoles") ] // 计算属性返回分组结果,强类型下可直接通过点语法访问category字段 var categories: [String: [Console]] { Dictionary(grouping: consoles, by: { $0.category }) } }
问题2:分类重复展示
重复展示的核心原因是之前弱类型字典+初始化顺序错误导致分组逻辑完全失效。Dictionary(grouping:by:)方法本身返回的字典key就是唯一的分组字段,只要模型和分组逻辑正确,不会出现重复分类。
修正模型后对应调整ContentView代码即可,注意结构体是类型,需要先创建实例才能访问属性,属性名要匹配大小写:
import SwiftUI struct ContentView: View { // 初始化数据源实例 private let dataSource = ConsoleList() var body: some View { NavigationView{ // 直接取分组字典的所有key,本身就是唯一值,不会重复 List(Array(dataSource.categories.keys).sorted(by: >), id: \.self){ category in NavigationLink(destination: ConsoleSection()) { HStack { Image(systemName: "folder.fill") .scaledToFit() .frame(height: 30) .cornerRadius(4) .padding(.vertical, 4) Text(category) } } .navigationTitle("游戏机收藏") } } } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } }
补充说明:
- 原代码中
let Con :[consoleDetails] = consoleList.Consoles属于无效代码,引用了不存在的consoleDetails类型和类属性Consoles,直接删除即可- 原代码中用VStack包裹单个Text属于冗余布局,换成HStack和Image同行排版效果更合理
- 如果需要跳转后展示对应分类下的主机列表,给
ConsoleSection增加分类入参,从categories字典中取对应key的数组传入即可。
内容的提问来源于stack exchange,提问作者Techtronis
相关产品推荐
相关产品推荐

