SwiftUI实现收藏按钮后点击MenuItem出现Optional解包nil崩溃求助
问题根因
你遇到的崩溃由两个核心问题共同触发:
- 全局状态
MenuModel从未被赋值,是空数组
你在ContentView中单独解码JSON到本地menu变量用于列表渲染,没有把数据同步到全局@EnvironmentObject修饰的MenuModel的items数组中,所以在ItemDetail中从menuModel.items查找对应条目时必然返回nil,加上你声明itemIndex为隐式强制解包类型Int!,直接触发崩溃。 - ID解码存在隐患
你的结构体中id默认赋值为UUID(),虽然Swift默认支持UUID字符串解码,但如果JSON中出现非法ID格式会直接解码失败,建议显式声明CodingKeys规避问题。
修复步骤
第一步:改造MenuModel,统一管理全局数据
final class MenuModel: ObservableObject { // 存储带分组的完整菜单数据,满足列表分组展示需求 @Published var sections: [MenuSection] = [] // 便捷访问所有平铺的菜单项 var allItems: [MenuItem] { sections.flatMap(\.items) } init() { // 初始化时直接解码JSON到全局状态,不需要各个页面单独解码 sections = Bundle.main.decode([MenuSection].self, from: "menu.json") } }
第二步:修改ContentView,移除本地JSON解码逻辑,统一使用全局状态
struct ContentView: View { @EnvironmentObject var menuModel: MenuModel // 删掉本地解码的let menu = ... 代码,改用menuModel.sections @State private var searchString = "" var searchResult : [MenuSection] { if searchString.isEmpty { return menuModel.sections } return menuModel.sections.map { menuSection in var menuSearch = menuSection menuSearch.items = menuSection.items.filter { $0.name.lowercased().contains(searchString.lowercased()) } return menuSearch }.filter { !$0.items.isEmpty } } // 其余视图代码不变 }
第三步:修改ItemDetail,移除强制解包,安全获取Binding
删掉原来的itemIndex: Int!相关代码,改用安全的Binding查找逻辑:
struct ItemDetail: View { @EnvironmentObject var menuModel: MenuModel var item: MenuItem let colors: [String: Color] = ["E": .green, "M": .yellow, "D": .red] // 安全查找对应条目的isFavorite绑定 private var favoriteBinding: Binding<Bool>? { for (sectionIdx, section) in menuModel.sections.enumerated() { if let itemIdx = section.items.firstIndex(where: { $0.id == item.id }) { return $menuModel.sections[sectionIdx].items[itemIdx].isFavorite } } return nil } var body: some View { ScrollView { VStack(){ // 原有展示代码不变 } .navigationTitle(item.name) .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .navigationBarTrailing) { // 安全解包后再使用绑定,不会再触发崩溃 if let favoriteBinding = favoriteBinding { FavoriteButton(isSet: favoriteBinding) } } } } } // 其余代码不变 }
第四步(可选):给数据结构体添加显式CodingKeys,提升编解码稳定性
struct MenuItem: Hashable, Codable, Equatable, Identifiable { var id = UUID() var name: String var author: String var level: [String] var isFavorite: Bool var description: String // 显式指定需要编解码的字段,避免计算属性参与编解码 enum CodingKeys: String, CodingKey { case id, name, author, level, isFavorite, description } // 其余计算属性、代码不变 }
内容的提问来源于stack exchange,提问作者Joseph
相关产品推荐
相关产品推荐

