选择器选择“0”无效并且没有关联的标签

Gal*_*ith 2 swift swiftui

我有一个包含交易类别的类(请参阅下面的示例代码),用户可以在设置过程中从中选择一个子集以在应用程序中使用。稍后在事务输入期间,如果未选择类中的第一个类别(在设置期间)并且选择器状态参数初始化为零,我会看到控制台消息“选择器:选择“0”无效并且没有关联的标签,这将给出未定义的结果”。事实上,如果状态参数初始化为 5 并且在设置过程中未选择第 5 个类别,它也会给出相同的警告。

我的问题是如何初始化状态参数,这样我就不会收到此选择器警告?

从控制台消息中我认为问题出在 ForEach 循环而不是条件。但如果删除条件if categories.catItem[item].catInUse == true {,我就看不到警告。

例如,在下面的代码中,如果在应用程序设置期间未选择类别Cat A(catInUse 为 false)并且交易条目选择器状态参数为, @State private var entryCat: Int = 0我将看到上面的控制台警告消息。类别Cat A仍然在列表中 - 只是没有显示。

struct getCategory: View {

    @EnvironmentObject var categories: Categories

    @State private var entryCat: Int = 0

    var body: some View {

        Section(header: Text("Select Category")) {

            Picker(selection: $entryCat, label: Text("")) {
                ForEach(0 ..< categories.catItem.count, id: \.self) { item in
                    if categories.catItem[item].catInUse == true {
                        Text(categories.catItem[item].catName)
                            .bold()
                    }
                }
            }.pickerStyle(MenuPickerStyle())
        }
    }
}



// working categories
struct CatModel:  Codable, Identifiable, Hashable {
    var id = UUID()
    var catNum: Int         // used by setup categories
    var catName: String     // category name
    var catTotal: Double    // category total
    var catBudget: Double   // category budget
    var catPix: String      // sf symbol
    var catInUse: Bool      // catInUse: true = category in use
    var catCustom: Bool     // true = custom category (can be deleted)
}


class Categories: ObservableObject {
    @Published var catItem: [CatModel] {
        didSet {   // save categories
            if let encoded = try? JSONEncoder().encode(catItem) {
                UserDefaults.standard.set(encoded, forKey: StorageKeys.workCat.rawValue)
            }
        }
    }

    var catCount: Int = 0
    init() {

        // read in category data
        if let catItem = UserDefaults.standard.data(forKey: StorageKeys.workCat.rawValue) {
            if let decoded = try? JSONDecoder().decode([CatModel].self, from: catItem) {
                self.catItem = decoded
                return
            }
        }

        catItem = []

        let item0 = CatModel(catNum: 0, catName: "Cat A", catTotal: 0.0, catBudget: 0, catPix: "a.circle", catInUse: false, catCustom: false)
        self.catItem.append(item0)

        let item1 = CatModel(catNum: 1, catName: "Cat B", catTotal: 0.0, catBudget: 0, catPix: "b.circle", catInUse: false, catCustom: false)
        self.catItem.append(item1)

        let item2 = CatModel(catNum: 2, catName: "Cat C", catTotal: 0.0, catBudget: 0, catPix: "c.circle", catInUse: false, catCustom: false)
        self.catItem.append(item2)

        let item3 = CatModel(catNum: 3, catName: "Cat D", catTotal: 0.0, catBudget: 0, catPix: "d.circle", catInUse: false, catCustom: false)
        self.catItem.append(item3)

        let item4 = CatModel(catNum: 4, catName: "Cat E", catTotal: 0.0, catBudget: 0, catPix: "e.circle", catInUse: false, catCustom: false)
        self.catItem.append(item4)

        let item5 = CatModel(catNum: 5, catName: "Cat F", catTotal: 0.0, catBudget: 0, catPix: "f.circle", catInUse: false, catCustom: false)
        self.catItem.append(item5)
    }
}
Run Code Online (Sandbox Code Playgroud)

Joa*_*son 6

我会避免使用索引来访问数组,而是使用 for every 循环。由于如果没有任何类别正在使用或值为 0 的类别未使用,您可能没有任何对象可供预选,因此我建议改用可选的选择属性。

最好将选择属性的类型设置为标识符而不是整个类型,并且由于您的模型符合,Identifiable因此 id 是一个不错的选择。

@State private var entryCat: CatModel.ID?
Run Code Online (Sandbox Code Playgroud)

另外,由于您想过滤模型对象,我更喜欢在计算属性中执行此操作,但当然直接在 中执行ForEach也是一种选择

var categoryList: [CatModel] {
    return categories.catItem.filter(\.catInUse)
}
Run Code Online (Sandbox Code Playgroud)

然后选择器代码可以更改为

Picker("", selection: $entryCat) {
    Text("<Nothing selected>").tag(nil as CatModel.ID?)
    ForEach(categoryList) { category in
        Text(category.catName)
            .tag(Optional(category.id))
    }
}.pickerStyle(MenuPickerStyle())
Run Code Online (Sandbox Code Playgroud)
  • 我使用Text第一个来表示为零时的状态entryCat,即没有选择任何内容。
  • 我用来tag保存用于设置的标识符entryCat
  • 由于entryCat是可选的,因此标签值也必须是可选的以匹配类型

有点偏离主题,但关于命名类型的一些建议,类型名称应该以大写字母开头,所以我会使用 GetCategory,模型的好名称是名词,不要缩写它,所以在这里我会使用 Category 而不是 CatModel