Reputation: 387
I have an app that stores transaction data with a category classification. The category names may be selected from a pool of enclosed names or added through text entry. The problem that I am having is with the selection of categories from pool of names.
The structure GetCategory is designed to select a transaction category. For example, buying some items at the grocery store might be categorized under a food category. The return value of this struct is to return the category name string.
Category selection is producing a warning message:
The transaction category selection process is producing the error Picker: the selection "nil" is invalid and does not have an associated tag, this will give undefined results.
I have tried the selection process with and without a tag and also with and without an optional, but don't seem to be getting any closer to resolving this warning and error.
The category-in-use flag catInuse is set true when selected.
struct GetCategory: View {
@EnvironmentObject var categories: Categories
@Binding var entryCat: CatModel?
var categoryList: [CatModel] {
return categories.catItem.filter(\.catInUse)
}
var body: some View {
ForEach(categoryList) { list in
Text(list.catName)
}
Section(header: Text("Select Category")) {
Picker("", selection: $entryCat) {
ForEach(categoryList) { category in
Text(category.catName)
.tag(Optional(category.id))
}
}.pickerStyle(MenuPickerStyle())
}
if let gotName = entryCat {
Text("Selected Category is \(gotName.catName)")
.padding(.top, 15)
}
}
}
Upvotes: 1
Views: 3286
Reputation: 1173
One way is to have a Divider
tag
ged with, in your case, CatModel?(nil)
as the last item in Picker
. Since Divider
s in menus are not rendered if they're the last item it will silence your warning and it will not be selectable:
Divider().tag(CatModel?(nil))
As an aside, in order to select a default value with data that is fetched from elsewhere, assign it within an .onAppear(perform:)
on Picker
:
.onAppear {
if self.entryCat == nil {
self.entryCat = self.categoryList.first
}
}
Upvotes: 2
Reputation: 36368
Upon reflextion, here is another approach that uses entryCat: CatModel?
and test it is
not nil
before displaying the Picker
. This way the option of nil
selection is
not possible.
The code below is just some example code that works for me, ie it does not produce the warnings. You need to ajust the code to suit your needs.
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)
}
struct ContentView: View {
@StateObject var categories = Categories()
@State var entryCat: CatModel? // <-- here
var body: some View {
VStack (spacing: 55) {
Text("Selected is \(entryCat?.catName ?? "no selection")") // <-- here for testing
GetCategory(entryCat: $entryCat)
.environmentObject(categories)
// for testing, change some catInUse to true
Button("click to test") {
// change the first false catInUse to true
if let first = categories.catItem.firstIndex(where: {!$0.catInUse}) {
categories.catItem[first].catInUse = true
entryCat = categories.catItem[first]
}
}
}
}
}
class Categories: ObservableObject {
// this is for testing purpose
@Published var catItem = [
CatModel(catNum: 0, catName: "Cat A", catTotal: 0.0, catBudget: 0, catPix: "a.circle", catInUse: false, catCustom: true),
CatModel(catNum: 1, catName: "Cat B", catTotal: 0.0, catBudget: 0, catPix: "b.circle", catInUse: true, catCustom: true),
CatModel(catNum: 2, catName: "Cat C", catTotal: 0.0, catBudget: 0, catPix: "c.circle", catInUse: true, catCustom: true)
//..... more CatModel
]
//..... more code
}
struct GetCategory: View {
@EnvironmentObject var categories: Categories
@Binding var entryCat: CatModel? // <-- here
var categoryList: [CatModel] {
categories.catItem.filter(\.catInUse)
}
var body: some View {
VStack {
Section(header: Text("Select Category")) {
if entryCat != nil { // <-- here
Picker("", selection: $entryCat) {
ForEach(categoryList) { category in
Text(category.catName).tag(category as CatModel?) // <-- here, needed
}
}.pickerStyle(MenuPickerStyle())
} else {
Text("No selections available").foregroundColor(.red) // <-- here is entryCat == nil
}
}
}
.onAppear {
// <-- try to make entryCat not nil, for testing if needed
if entryCat == nil {
if let theEntryCat = categoryList.first {
entryCat = theEntryCat
}
}
}
}
}
Upvotes: 1