SwiftUI Picker refreshed Array nicht

Diese Seite verwendet Cookies. Durch die Nutzung unserer Seite erklären Sie sich damit einverstanden, dass wir Cookies setzen. Weitere Informationen

  • SwiftUI Picker refreshed Array nicht

    Hallo,
    ich habe ein Problem mit dem Refresh eines Pickers in einer App.

    Aufbau.:
    1. Klasse mit einem String Array, welches komplett gefüllt ist mit 60 Strings.
    2. Klasse (UI) ist ein Screen mit einem DatePicker und einem Picker
    3. Klasse ist beinhaltet eine Funktion die Daten aus einer Firebase DB holt.

    Ablauf:
    In der 2. Klasse stelle ich einen Kalender dar. Wenn man einen Tag auswählt, wird in der onChange Methode die Funktion der 3. Klasse mit dem gewählten Tag als Parameter aufgerufen.
    Die Funktion liest dann in der DB die vorhandenen Einträge zu diesem Tag aus und modifiziert das String-Array aus Klasse 1. (löscht Objekte aus dem Array, zu denen ein Eintrag in der DB gefunden wurde)
    Der Picker in der 2. Klasse gibt über eine ForEach Schleife die Strings des Arrays aus.

    Problem:
    Das Picker Element wird nicht direkt aktualisiert.. es hängt quasi immer einen Klick hinterher..
    Wähle ich zB Tag 25, dann 26, dann 27, bekomme ich bei 26 den Stand vom 25., dann bei 27. den Stand vom 26..

    Ich habe bereits versucht, den Ticker über .id zu aktualisieren, mit eine if-else und einem getoggelten Bool Wert den Picker zu updaten.
    Bringt alles nichts.
    Ich habe auch versucht den Aufruf der Methode in der onChange in einer DispatcherQueue.main.sync zu packen, quasi um einfach zu warten.. aber dann crasht die App komplett, obwohl die DB Abfragen durchgehen.
    Als letztes habe ich jetzt die 1. Klasse als ObservableObject deklariert, das Array als @Published und in der 2. Klasse als @StateObject die Klasse eingebunden..

    Es bringt alles nichts.. ich hänge immer einen Tag zurück.

    Wo genau ist mein Denkfehler? Bekomme ich das irgendwie über saubere @State, @Binding Einstellungen irgendwie hin?
  • Bin zwar mit SwiftUI noch nicht firm, aber soweit ich gesehen habe nutzt man hier vorzugsweise DataBinding und Observable Collections/Objects um die aktualisieren von Daten und GUI zu „automatisieren“.

    Aus anderen Programmiersprachen kenne ich das DataBinding auch, weshalb ich diese Technik definitiv bevorzugen würde.
  • v1n2 schrieb:

    Problem:

    Das Picker Element wird nicht direkt aktualisiert.. es hängt quasi immer einen Klick hinterher..
    Was wird denn nicht aktualisiert, der Picker oder der Label des Picker? Ausserdem welchen Pickerstyle verwendest du?

    Im allgemeinen, du solltest bei jeder Aktualisierung deiner Daten deine Elemente benachrichtigen. Dabei gibt es ein paar Fussangeln, z.b. von Swift. Wenn Du die Daten in einer init() erstellst oder aktualisierst, geht quasi keine Benachrichtigung raus. Am vernünftigsten funktioniert das ganze dann sowieso nur für Klassen, nicht für Structs. Klassen werden in Structs seit V2, mit @StateObject deklariert, in V1 mit @ObservedObject. Wobei die Konvention aus V1 für Klassenübergaben aus V1 beibehalten wurde.

    Wenn sich was ändert, dann muss auch die UI mitbekommen, dass sich was geändert hat. Da kannst du entweder Standard Nachrichten versenden, oder selbst Hand anlegen. Wenn alles nichts hilft, kannst du auch die UI zwingen, sich selbst zu aktualisieren.

    Insgesamt geb ich dir schon recht, Picker, insbesondere eingebettet in Forms, sind ein Graus
  • 1. und 3. sind ja Klassen..

    1. ist quasi das Model.. ich habe eine class TimeSlot, in der ich dann das Array deklariert habe..
    3. ist meine class FBfunc, in der ich alle FB-Zugriffsmethoden deklariert habe..

    und 2. ist meine View, also ein struct, keine class.

    ich hatte es so verstanden, dass dann meine class 1 als ObservableObject deklariert werden muss, das Array als Published.
    Ist das schon mal soweit korrekt?

    in meiner View 2 deklariere ist das Array dann als @StateObject, um Änderungen mitzubekommen, oder?
    Und in der 3. Class nutze ich auch @StateObject? ich habe dort immer mittels TimeSlot.instance.slotArray zugegriffen. (in der TimeSlots oben noch ein static let instance = TimeSlot() )
  • Um mal nach der neuen Methode zu reden, in der View, wenn du ein klasse verwenden möchtest, du du einmalig initialisieren möchtest, nutzt du ein @StateObject alternativ kannst du auch ein @EnvironmentObject deklarieren. Wenn du das Stateobject in eine Klasse durchreichen möchtest, dann musst du in der anderen View ein @ObservedObject verwenden. Damit Du es einen @Observed- oder @StateObject zuweisen kannst, muss es als Observable deklariert. sein. Value Types, kannst du mit @State oder @Binding deklarieren, jedoch keine Referenztypes.

    @Published ist nur eine bequemere Methode, andere zu informieren, dass sich etwas geändert hat. Kannst aber auch einfach selbst implementieren. Ein Problem mit Published ist bspw. dass du damit keine Computed-Vars verwenden kannst.