将状态参数保存回 Class

Saving State Parameter back to Class

我正在尝试创建两个视图来显示正在使用未使用的类别列表。以下代码仅显示未使用的类别。

我希望能够点击一个类别并让它从未使用视图中消失并出现在正在使用 视图,反之亦然。我的视图正在处理列表和按钮,但我无法将状态参数保存回我的 class 结构。我在函数 saveStatus().

中看到错误“无法分配给 属性:'item' 是一个 'let' 常量”

我正在使用 ForEach(0.. 来显示类别,但列表中存在空白,因为一些类别被分配给使用中视图而不是未使用视图。所以现在过滤是更流畅地显示列表。但我遇到的问题是如何保存 catShow 状态的变化(我用来区分视图的 Bool)。我正在使用 ForEach() 的索引形式来帮助做到这一点。

感谢您的见解。

struct CatModel:  Codable, Identifiable, Hashable {
    var id = UUID()
    var catName: String
    var catPix: String
    var catShow: Bool        // false = not in use; true = in use
}


class Categories: ObservableObject {

     @Published var filteredInUse: [CatModel] = []
     @Published var filteredNotInUse: [CatModel] = []
     @Published var catItem: [CatModel] {
        didSet {
            if let encoded = try? JSONEncoder().encode(catItem) {      // save categories
                UserDefaults.standard.set(encoded, forKey: "workCat")
            }
        }
    }

    init() {
        self.catItem = []

        // catShow: 1 = category available for use; 2 = category currently being used
        let item0 = CatModel(catName: "Category 1", catPix: "bed.double.fill", catShow: false)
        self.catItem.append(item0)

        let item1 = CatModel(catName: "Category 2", catPix: "cart", catShow: false)
        self.catItem.append(item1)

        let item2 = CatModel(catName: "Category 3", catPix: "airplane", catShow: false)
        self.catItem.append(item2)

        let item3 = CatModel(catName: "Category 4", catPix: "tram", catShow: false)
        self.catItem.append(item3)

        let item4 = CatModel(catName: "Category 5", catPix: "bus.fill", catShow: false)
        self.catItem.append(item4)

        if let encoded = try? JSONEncoder().encode(catItem) {
            UserDefaults.standard.set(encoded, forKey: "workCat")
        }
        self.updateStatus()
    }

    func updateStatus() {
        filteredInUse = catItem.filter({ (user) -> Bool in
            return user.catShow == true
        })

        filteredNotInUse = catItem.filter({ (user) -> Bool in
            return user.catShow == false
        })
    }
}


struct ContentView: View {
    @StateObject var categories = Categories()

    @State private var showStatus: Bool = false

    var body: some View {

        VStack ( spacing: 6) {

            Text("Not In Use Categories")

            ForEach(categories.filteredNotInUse, id: \.id) { item in

                Button(action: {

                    showStatus = true

                }){

                HStack {
                    Image(systemName: item.catPix)
                        .resizable()
                        .frame(width: 18, height: 18)

                    Text(item.catName)
                        .padding(.leading, 40)
                    }
                }
            //    categories.catItem[index].catShow = showStatus
             //   item.catShow = showStatus
            }
        }
    }
}

您正在为 CatModel 使用 struct:结构是不可变的,因此您不能改变其中的变量。

第一个建议是在 Core Data 中创建 CatModel:如果这样做,您的代码将起作用。如果 CatModelclass.

,它也会起作用

如果您不想/不能使用 Core Data 或 class,解决方案可以是将包含 catShow = falseCatModel 完全替换为另一个实例其中 catShow = true.

首先,在CatModel中创建一个属性来提供一个新实例,改变catShow值:

struct CatModel:  Codable, Identifiable, Hashable {
    var id = UUID()
    var catName: String
    var catPix: String
    var catShow: Bool        // false = not in use; true = in use
    
    /// Provides a new instance, toggling `catShow`.
    var toggleCatShow: CatModel {

        // Note: by creating a new instance, the new id will be different
        CatModel(catName: catName, catPix: catPix, catShow: !catShow)
    }
}

其次,在 Categories 视图模型中创建一个函数,该函数将在相同位置用新实例替换现有实例:

class Categories: ObservableObject {

     @Published var filteredInUse: [CatModel] = []
     @Published var filteredNotInUse: [CatModel] = []
     @Published var catItem: [CatModel] {
        didSet {
            if let encoded = try? JSONEncoder().encode(catItem) {      // save categories
                UserDefaults.standard.set(encoded, forKey: "workCat")
            }
        }
    }

    init() {
        self.catItem = []

        // catShow: 1 = category available for use; 2 = category currently being used
        let item0 = CatModel(catName: "Category 1", catPix: "bed.double.fill", catShow: false)
        self.catItem.append(item0)

        let item1 = CatModel(catName: "Category 2", catPix: "cart", catShow: false)
        self.catItem.append(item1)

        let item2 = CatModel(catName: "Category 3", catPix: "airplane", catShow: false)
        self.catItem.append(item2)

        let item3 = CatModel(catName: "Category 4", catPix: "tram", catShow: false)
        self.catItem.append(item3)

        let item4 = CatModel(catName: "Category 5", catPix: "bus.fill", catShow: false)
        self.catItem.append(item4)

        if let encoded = try? JSONEncoder().encode(catItem) {
            UserDefaults.standard.set(encoded, forKey: "workCat")
        }
        self.updateStatus()
    }

    func updateStatus() {
        filteredInUse = catItem.filter({ (user) -> Bool in
            return user.catShow == true
        })

        filteredNotInUse = catItem.filter({ (user) -> Bool in
            return user.catShow == false
        })
    }
    
    /// Replaces an instance of `CatModel` by changing its `catShow` value.
    func setCatShow(forId id: UUID) {
        let index = catItem.firstIndex { [=11=].id == id }
        if let index = index {
            catItem.replaceSubrange(index...index, with: [catItem[index].toggleCatShow])
        }
        
        // Refresh the filtered arrays
        updateStatus()
    }
}

最后调用Button()里面的方法:

struct ContentView: View {
    @StateObject var categories = Categories()

    @State private var showStatus: Bool = false

    var body: some View {

        VStack ( spacing: 6) {

            Text("Not In Use Categories")

            ForEach(categories.filteredNotInUse, id: \.id) { item in

                Button(action: {
                    withAnimation {
                        showStatus = true
                        
                        // Call the method on the view model
                        categories.setCatShow(forId: item.id)
                    }

                }){

                HStack {
                    Image(systemName: item.catPix)
                        .resizable()
                        .frame(width: 18, height: 18)

                    Text("\(item.catName), in use: \(String(describing: item.catShow))")
                        .padding(.leading, 40)
                    }
                }
            }
        }
    }
}

点击类别 3 之前:

点击类别 3 后:

正在替换:

ForEach(categories.filteredNotInUse, id: \.id) { item in

与:

ForEach(categories.catItem, id: \.id) { item in

让我们看看 ALL 行及其 catShow 值。在这种情况下:

点击类别 3 之前:

点击类别 3 后: