如何为承载CoreData数据的结构体添加可视图修改的isSelected变量
问题描述
我正在创建一个ViewModel用于存储从数据库获取的数据,获取后将其映射为包装类型的数组。现在想给FolderWrapper添加isSelected变量,并能在视图中通过isSelected.toggle()修改它,但尝试时遇到了"folder是不可变的"错误,用绑定或mutating方法也没解决,该怎么做?
现有代码
ViewModel代码
class FoldersVM : ObservableObject { let manager = PersistenceController() @Published var folders: [FolderWrapper] = [] func retrieveFolders() { folders = manager.retrieveFolders().map(FolderWrapper.init) } }
FolderWrapper结构体
struct FolderWrapper: Hashable { var folder: Folder var name: String { return folder.name ?? "error" } var index: Int { return folder.index } }
视图代码
struct FolderView1: View { var folders: [FolderWrapper] var body: some View { LazyVGrid(columns: [GridItem(.adaptive(minimum: 10))]) { ForEach(folders, id: \.self) { folder in fw(folder: folder) } } } func fw(folder: FolderWrapper) -> some View{ Image(systemName: "folder") .onTapGesture { //此处尝试选择但报错:folder是不可变的 } } }
解决方案
步骤1:给FolderWrapper添加isSelected并维护Hashable一致性
因为FolderWrapper遵循Hashable,添加isSelected后要确保哈希值和相等判断不受该属性影响(选中状态不该改变元素唯一性):
struct FolderWrapper: Hashable { var folder: Folder var isSelected = false // 新增选中状态属性 var name: String { folder.name ?? "error" } var index: Int { folder.index } // 自定义相等判断,仅对比底层folder static func == (lhs: FolderWrapper, rhs: FolderWrapper) -> Bool { lhs.folder == rhs.folder // 需确保Folder本身遵循Equatable } // 自定义哈希计算,仅基于folder func hash(into hasher: inout Hasher) { hasher.combine(folder) } }
步骤2:通过绑定传递并修改选中状态
视图需要拿到FolderWrapper的绑定,推荐直接依赖ViewModel来实现状态同步:
struct FolderView1: View { @ObservedObject var vm: FoldersVM // 持有ViewModel实例 var body: some View { LazyVGrid(columns: [GridItem(.adaptive(minimum: 100))]) { ForEach($vm.folders) { $folder in // 通过$获取元素绑定 fw(folder: $folder) } } } func fw(folder: Binding<FolderWrapper>) -> some View { // 根据选中状态切换图标样式 Image(systemName: folder.wrappedValue.isSelected ? "folder.fill" : "folder") .onTapGesture { folder.wrappedValue.isSelected.toggle() // 直接修改绑定的值 } } }
关键说明
- 自定义
Hashable实现:默认逻辑会把isSelected纳入判断,导致选中状态变化时ForEach误判为新元素,引发不必要的视图刷新,因此要只基于folder做判断。 - 绑定传递:
ForEach($vm.folders)能直接获取数组元素的绑定,视图中的修改会同步回ViewModel的@Published数组,自动触发视图更新。 - 实体协议支持:如果
Folder是Core Data/SwiftData实体,需确保它遵循Equatable和Hashable,Core Data可通过Xcode自动生成相关实现。
内容的提问来源于stack exchange,提问作者crost
相关产品推荐
相关产品推荐

