You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为承载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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 04:17:41