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

SwiftUI中用ForEach从Set生成Toggle列表及Core Data存储问题

问题解答:Toggle列表与Core Data Transformable存储

一、使用Set创建Toggle列表

Set是无序集合,无下标访问能力,且无法直接通过下标创建Binding,需通过以下方式处理:

  1. 将Set转为数组供ForEach遍历(利用AllScopes已遵循Identifiable的特性)
  2. 自定义Binding,通过元素的id定位Set中的元素,修改时替换原元素(值类型属性修改需生成新实例)

示例代码:

struct YearlyReportPage6: View {
    @Binding var scopes: Set<AllScopes>
    
    var body: some View {
        VStack {
            // 把Set转为数组,遍历每个Identifiable元素
            ForEach(Array(scopes)) { scope in
                Toggle(isOn: Binding(
                    // 获取当前元素的notify状态
                    get: { scopes.first(where: { $0.id == scope.id })?.notify ?? false },
                    // 修改时生成新实例,替换Set中的原元素
                    set: { newValue in
                        guard let existing = scopes.first(where: { $0.id == scope.id }) else { return }
                        var updatedScope = existing
                        updatedScope.notify = newValue
                        scopes.remove(existing)
                        scopes.insert(updatedScope)
                    }
                )) {
                    Text(scope.name)
                }
            }
        }
    }
}

二、将数组/字典存储到Core Data的Transformable字段

步骤1:让数据类型遵循Codable

确保数组元素或字典的键值类型遵循Codable协议,以AllScopes为例:

struct AllScopes: Identifiable, Hashable, Codable {
    var id: UUID
    var name: String
    var notify: Bool
}

(字典如[String: Bool]本身已满足Codable,无需额外修改)

步骤2:配置Core Data模型

在Xcode的数据模型编辑器中:

  • 创建一个Transformable类型的属性(例如命名为scopesData)
  • 在右侧检查器中:
    • 将Transformer设置为NSSecureUnarchiveFromDataTransformer(苹果推荐的安全转换方式)
    • 勾选Allows External Storage(可选,优化大容量数据的存储性能)

步骤3:实体类中添加转换逻辑

在Core Data实体的扩展中,添加计算属性来处理数组/字典与Transformable字段的自动转换:

// 假设你的Core Data实体名为Report
extension Report {
    // 数组与Transformable字段的转换
    var scopesArray: [AllScopes] {
        get {
            guard let data = scopesData as? Data else { return [] }
            return try? JSONDecoder().decode([AllScopes].self, from: data) ?? []
        }
        set {
            scopesData = try? JSONEncoder().encode(newValue)
        }
    }
    
    // 如果是字典,示例如下:
    var notifyDict: [String: Bool] {
        get {
            guard let data = dictData as? Data else { return [:] }
            return try? JSONDecoder().decode([String: Bool].self, from: data) ?? [:]
        }
        set {
            dictData = try? JSONEncoder().encode(newValue)
        }
    }
}

注意事项

  • 所有嵌套的数据类型必须都遵循Codable,否则编码/解码会失败
  • 避免使用旧的NSKeyedUnarchiveFromDataTransformer,存在安全风险
  • 若需自定义转换逻辑,可以创建继承自NSSecureUnarchiveFromDataTransformer的子类并注册

内容的提问来源于stack exchange,提问作者mihema

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 07:15:34