SwiftUI in der Praxis – Teil 4

Binding selbst definieren

Bindings gehören zu den wichtigsten und spannendsten neuen Techniken, die das SwiftUI-Framework mit sich bringt. In der Regel ist ihr Einsatz unkompliziert und sorgt dafür, Daten als Managed Reference an eine andere View zu übergeben, ohne diese Daten kopieren und selbst synchron halten zu müssen; genau jene Aufgabe übernimmt schließlich das Binding für uns.

Es gibt aber Szenarien, in denen das einfache Übergeben einer Source of Truth an eine Binding-Property nicht funktioniert. Ein simples Beispiel dazu seht ihr im folgenden Listing.

struct ContentView: View {
    @State private var selectedDate: Date?
    
    var body: some View {
        // Fehler: selectedDate ist ein Optional.
        DatePicker(selection: $selectedDate, label: {
            Text("Date")
        })
        .padding()
    }
}

Die Basis darin stellen ein Date-Picker und eine State-Property dar. Die State-Property speichert eine Instanz vom Typ Date, ist aber als Optional deklariert; der Status kann also auch nil entsprechen.

Und genau dieser Umstand ist das Problem. Denn Instanzen vom Typ DatePicker benötigten ein Binding auf Basis eines Date; und das darf nicht optional sein! Entsprechend kann die State-Property so nicht dem Date-Picker übergeben werden.

Was also tun? Die Lösung besteht darin, ein eigenes Binding mithilfe des Binding-Typs zu erstellen. Ähnlich einer Computed Property definiert man hierbei selbst, welcher Wert beim Zugriff auf das Binding zurückgegeben wird und was beim Setzen eines neuen Werts geschieht.

Das folgende Listing zeigt, wie man ein passendes Binding für den Date-Picker selbst programmatisch erzeugt. Wichtig ist hierbei, explizit den Typ anzugeben, der für die Werte des Bindings bestimmt ist; in diesem Fall also Date.

let selectedDateBinding = Binding<Date>(get: {
    if self.selectedDate != nil {
        return self.selectedDate!
    }
    return Date()
}, set: {
    self.selectedDate = $0
})

Über den Initializer von Binding gibt man an, was beim Lesen und Schreiben des Bindings geschehen soll; dazu sind die beiden Parameter get beziehungsweise set da.

In dem anfangs beschriebenen Beispiel lässt sich das Problem der optionalen State-Property wie folgt lösen: Nur, wenn die State-Property über einen Wert verfügt, wird eben der mittels Binding weitergegeben, andernfalls schlicht das aktuelle Datum. Das stellt sicher, dass der Date-Picker immer einen validen Wert erhält, so wie es in der zugehörigen Structure definiert ist und vorausgesetzt wird.

Die Implementierung des Setter unseres eigenen Bindings ist noch einfacher. So soll ein über den Date-Picker ausgewählter Wert schlicht direkt der State-Property zugewiesen werden.

Um also das Anfangsbeispiel funktionsfähig zu machen, zeigt das folgende Listing noch einmal das Zusammenspiel zwischen View und Einbindung des eigenen Bindings. Letzteres wird hierbei als Teil der body-Property umgesetzt. Das Binding wird dann direkt dem Picker als Parameter übergeben.

struct ContentView: View {
    @State private var selectedDate: Date?
    
    var body: some View {
        let selectedDateBinding = Binding<Date>(get: {
            if self.selectedDate != nil {
                return self.selectedDate!
            }
            return Date()
        }, set: {
            self.selectedDate = $0
        })
        return DatePicker(selection: selectedDateBinding, label: {
            Text("Date")
        })
        .padding()
    }
}

Fazit

In Szenarien wie den in diesem Artikel beschriebenen reicht das „einfache“ Binding-Konzept nicht mehr aus. Erfreulicherweise ist es aber ohne Probleme möglich, eigene Bindings auf Basis des Binding-Typs zu definieren, für die sich exakt festlegen lässt, was beim Zugriff auf das Binding geschehen soll. Das lässt sich auch mit zusätzlichen Aktionen verknüpfen, um beispielsweise beim Setzen eines Werts mittels Binding weitere Funktionen auszuführen.

Euer Thomas

Bisherige Artikel in dieser Serie


Kommentare

4 Antworten zu „SwiftUI in der Praxis – Teil 4“

  1. Avatar von Torsten Zimmermann
    Torsten Zimmermann

    Hallo Thomas,

    könntest du bitte in einer Artikelserie Links zu den vorherigen Teilen mit unterbringen. Das würde die Navigation deutlich vereinfachen.

    Vielen Dank und viele Grüße,
    Torsten

    1. Hallo Torsten,

      besten Dank für die Anregung, das ist definitiv eine gute und sinnvolle Idee! 🙂 Ich ergänze die entsprechenden Links zukünftig immer am Ende eines Artikels, die bestehenden Artikel (zumindest von den Serien, die noch aktiv sind) aktualisiere ich in der nächsten Zeit.

      Mit besten Grüßen,
      Thomas

  2. Danke für diese Anleitung, dies hat mir sehr geholfen, allerdings habe ich noch nicht verstanden, wofür $0 steht?

    1. $0 ist in Swift eine Möglichkeit zum Schnellzugriff auf den neuen Wert, den man über den Setter erhält. Man kann alternativ auch einen temporären Bezeichner für diesen Wert festlegen, ich nutze aber gerne diese Schnellzugriffsmöglichkeit mittels $-Syntax.

      Es handelt sich hierbei um ein Feature von Closures, die so den gezeigten Schnellzugriff auf Parameter ermöglichen.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert