Search code examples
core-dataswiftuipicker

How do I include a Circle on the same line as my Picker selection in SwiftUI in for example an HStack?


When mocking my app, I had this code for a picker, (in a Form so I'd get the navigation view for the selections):

Picker("Training Group", selection: $numberOfPeople) {
    ForEach(2 ..< 10) { number in
        HStack {
            Circle().fill(Color.systemYellow)
                .frame(width: 16, height: 16)
            Text("Group \(number)")
        }
    }

and this works: in both the picker and the subsequent nav view have nice little circles, just like in Apple's own Calendar app - when selecting a calendar for the event, each selection is noted with its own colour "label" / "tag".

Now I'm trying to implement this with an app using core data, but the code no longer works. The picker ONLY accepts a Text view and anything else causes the bindings to not work and the selected "level" in my case is not saved to the managedObjectContext.

This is the struct in my CoreData attempt:

struct AthleteProfile: View {

    @Environment(\.managedObjectContext) private var viewContext
    
    @FetchRequest(
        sortDescriptors: [NSSortDescriptor(keyPath: \Level.name, ascending: true)],
        animation: .default)

    private var levels: FetchedResults<Level>
    
    @ObservedObject var athlete: Athlete

    var body: some View {
        Form {
            Text(athlete.lastName ?? "Unknown")
            Picker("Level", selection: $athlete.level) {
                ForEach(levels) { (level: Level?) in

                        /// I want to place this Text view into an HStack like this:

                    HStack {
                        Circle().fill(Color.green)
                            .frame(width: 16, height: 16)
                        Text("\(level?.name ?? "Unassigned")").tag(level)
                    }

                        /// but the above fails. This below works:

                    Text("\(level?.name ?? "Unassigned")").tag(level)

                       /// of course, I use only one of them in the app, the other commented out.

                }
            }
        }
        .onDisappear(perform: {
            saveContext()
        })
    }
    
    private func saveContext() {
        do {
            try viewContext.save()
        } catch {
            // Replace this implementation with code to handle the error appropriately.
            // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
            let nsError = error as NSError
            fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
        }
    }
}

What might be the problem?


Solution

  • This is probably because you try to apply .tag(level) to an inner view:

    HStack {
        Circle().fill(Color.green)
            .frame(width: 16, height: 16)
        Text("\(level?.name ?? "Unassigned")").tag(level)
    }
    

    The below example works because tag is applied to the top level view in ForEach:

    Text("\(level?.name ?? "Unassigned")").tag(level)
    

    A solution may be to attach tag to HStack:

    HStack {
        Circle().fill(Color.green)
            .frame(width: 16, height: 16)
        Text("\(level?.name ?? "Unassigned")")
    }
    .tag(level)