TEAM LICENSES: Save money and learn new skills through a Hacking with Swift+ team license >>

SOLVED: Can somebody help me with my Picker?

Forums > SwiftUI

@HRait  

Hi everyone,

I am working on an app and would like to add a picker, but I get the message "Picker: the selection "nil" is invalid and does not have an associated tag, this will give undefined results" in the log. What can I do? Thx

Destination.swift

import Foundation
import SwiftData

@Model
class Destination {
    var name: String
    var details: String
    var date: Date
    var priority: Int

    @Relationship(deleteRule: .cascade) var personen = [Personen]()
    @Relationship(deleteRule: .cascade) var kosten = [Kosten]()

    init(name: String = "", details: String = "", date: Date = .now, priority: Int = 2) {
        self.name = name
        self.details = details
        self.date = date
        self.priority = priority
    }
}

Personen.swift

import SwiftUI
import SwiftData

@Model
class Personen: Identifiable {
    var id: UUID = UUID()
    var name: String
    var destination: Destination?
    @Relationship(deleteRule: .cascade) var kosten = [Kosten]()

    init(id: UUID, name: String = "", destination: Destination? = nil, kosten: [Kosten] = [Kosten]()) {
        self.id = id
        self.name = name
        self.destination = destination
        self.kosten = kosten
    }
}

Kosten.swift

import SwiftUI
import SwiftData

@Model
class Kosten {
    var name: String
    var beitrag: Double
    var anzahl: String
    var destination: Destination?
    var personen: Personen?

    init(name: String, beitrag: Double, anzahl: String, destination: Destination? = nil, personen: Personen? = nil) {
        self.name = name
        self.beitrag = beitrag
        self.anzahl = anzahl
        self.destination = destination
        self.personen = personen
    }
}

EditDestinationView

import SwiftUI
import SwiftData

struct EditDestinationView: View {
    @Bindable var destination: Destination

    @State private var newPersonName = ""
    @State private var newKostenName = ""
    @State private var newKostenBeitrag: Double = .zero
    @State private var newKostenAnzahl = ""

    // For the Popup View
    @State private var showingPopover = false

    @State private var multiSelection: UUID?

    var body: some View {

        Form {
            TextField("Name", text: $destination.name)
            TextField("Details", text: $destination.details, axis: .vertical)
            DatePicker("Date", selection: $destination.date)
                .datePickerStyle(.graphical)
                .background(.background, in: .rect(cornerRadius: 10))
                .accentColor(.purple)

            Section("Währung") {
                Picker("Währung", selection: $destination.priority) {
                    Image(systemName: "dollarsign").tag(1)
                    Image(systemName: "eurosign").tag(2)
                    Image(systemName: "bitcoinsign").tag(3)
                }
                .pickerStyle(.segmented)
            }
            Section("Personen") {
                HStack {
                    TextField("Füge eine Person hinzu", text: $newPersonName)

                    Button("Hinzufügen", action: addSight)
                        .foregroundStyle(.purple)
                }
                ForEach(destination.personen, id: \.id) { personen in
                    Text(personen.name)
                }
            }
            Section("Kosten Hinzufügen") {
                TextField("Kosten Name", text: $newKostenName)

                HStack {
                    if destination.priority == 1 {
                        Image(systemName: "dollarsign")
                    }
                    else if destination.priority == 2 {
                        Image(systemName: "eurosign")
                    }
                    else {
                        Image(systemName: "bitcoinsign")
                    }

                    TextField("", value: $newKostenBeitrag, formatter: numberFormatter)
                        .keyboardType(.decimalPad)

                    TextField("Anzahl je:", text: $newKostenAnzahl)
                }

                Button("Hinzufügen", action: addKosten)
                    .foregroundStyle(.purple)

                ForEach(destination.kosten, id: \.self) { kosten in
                    VStack (alignment: .leading) {
                        Text(kosten.name)
                            .font(.title3)
                            .fontWeight(.bold)

                        Text("Anzahl: \(kosten.anzahl)")
                        HStack {
                            Text("Beitrag (je): \(kosten.beitrag)")

                            if destination.priority == 1 {
                                Image(systemName: "dollarsign")
                            }
                            else if destination.priority == 2 {
                                Image(systemName: "eurosign")
                            }
                            else {
                                Image(systemName: "bitcoinsign")
                            }
                        }
                        Picker("Personen auswählen", selection: $multiSelection) {

                            Text("").tag("")

                            ForEach(destination.personen, id: \.id) { personen in
                                Text(personen.name).tag(personen.id)
                            }
                        }
                        .pickerStyle(.menu).foregroundStyle(.purple)
                    }
                }
            }
        }
        .navigationTitle("Edit Destination")
        .navigationBarTitleDisplayMode(.inline)
    }

    func addSight() {
        guard newPersonName.isEmpty == false else { return }

        withAnimation {
            let person = Personen(id: UUID(), name: newPersonName)
            destination.personen.append(person)
            newPersonName = ""
        }
    }

    func addKosten() {
        /// Um Kosten hinzuzufügen
        guard newKostenName.isEmpty == false else { return }
        guard newKostenAnzahl.isEmpty == false else { return }

        withAnimation {
            let kosten = Kosten(name: newKostenName, beitrag: newKostenBeitrag, anzahl: newKostenAnzahl)
            destination.kosten.append(kosten)
            newKostenName = ""
            newKostenBeitrag = .zero
            newKostenAnzahl = ""
        }
    }

    /// Number Formatter
    var numberFormatter: NumberFormatter {
        let formatter = NumberFormatter()
        formatter.numberStyle = .decimal
        formatter.maximumFractionDigits = 2

        return formatter
    }
}

#Preview {
    do {
        let config = ModelConfiguration(isStoredInMemoryOnly: true)
        let container = try ModelContainer(for: Destination.self, configurations: config)
        let example = Destination(name: "Tacco Laden", details: "Tacco Laden mit der Truppe.")
        return EditDestinationView(destination: example)
            .modelContainer(container)
    } catch {
        return Text("Failes to create container: \(error.localizedDescription)")
    }
}

2      

Consider adding code to your Destination class describing the Image that should be used in views.

Then you can replace this....

HStack {
        if destination.priority == 1 {
            Image(systemName: "dollarsign")
        }
        else if destination.priority == 2 {
            Image(systemName: "eurosign")
        }
        else {
            Image(systemName: "bitcoinsign")
        }
    //. .... more code here.....

with cleaner code such as

HStack {
    // Just declare what you want to see!
    // You want to see the destination image, yes? Then tell this to SwiftUI.
    destination.image // <-- this returns an Image object but is defined INSIDE your Destination class
    // .... more code here....

3      

I am not sure where you initalize the EditDestinationView struct?

The EditDestinationView struct requires a bindable Destination object. So this tells me some other view is responsible to verify the Destination object that you give to the view is NOT nil.

Are you checking this object before passing it to the EditDestinationView struct? Do not pass in a nil Destination object.

2      

@HRait  

Thx I think i fix it but I have another issue.

The Fix

Picker("Kosten auswählen", selection: $kostenAuswahl) {

                            Text("").tag(nil as Kosten?)

                            ForEach(destination.kosten, id: \.self) { kosten in
                                Text(kosten.name).tag(kosten as Kosten?).bold()
                            }
                        }
                        .pickerStyle(.menu).foregroundStyle(.purple)
                    }

Now my problem is in the list of people where the picker is. If I select a "cost" for the person, it is selected for each person. Does anyone have a solution to this?

2      

Hacking with Swift is sponsored by RevenueCat.

SPONSORED Take the pain out of configuring and testing your paywalls. RevenueCat's Paywalls allow you to remotely configure your entire paywall view without any code changes or app updates.

Learn more here

Sponsor Hacking with Swift and reach the world's largest Swift community!

Reply to this topic…

You need to create an account or log in to reply.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.