Hinweis: Der folgende Artikel hilft Ihnen weiter: Arbeiten mit @Binding in SwiftUI
Zurück zum Blog
Eine Bindung in SwiftUI ist eine Verbindung zwischen einem Wert und einer Ansicht, die ihn anzeigt und ändert. Sie können Ihre eigenen Bindungen mit dem @Binding-Eigenschaftswrapper erstellen und Bindungen an Ansichten übergeben, die sie erfordern. In diesem Tutorial zur Entwicklung mobiler Apps besprechen wir, wie Bindungen in SwiftUI funktionieren.
Hier erfahren Sie, worauf wir eingehen:
- Was ist eine Bindung und warum braucht man sie?
- Der Unterschied zwischen einer Bindung und @Binding
- So erhalten Sie eine Bindung von anderen Eigenschaften-Wrappern wie @State
- Welche Eigenschaften-Wrapper stellen Bindungen bereit und wann werden sie verwendet?
- So stellen Sie eine Bindung für ein UI-Element wie TextField bereit
Eine Bindung ist im Wesentlichen eine Verbindung zwischen einem Wert, z. B. einem Bool, und einem Objekt oder UI-Element, das ihn anzeigt und ändert, z. B. einem Schieberegler oder einem Textfeld. Die Verbindung ist bidirektional, sodass das UI-Element den Wert ändern kann und der Wert das UI-Element ändern kann.
In SwiftUI können Sie Bindungen auf zwei Arten erstellen:
- Mit dem @Binding-Eigenschaftswrapper, der eine Bindung erstellt, diese aber nicht speichert
- Mit anderen Eigenschafts-Wrappern wie @State, die eine Bindung erstellen und auch deren Wert speichern
Basierend auf dem ersten Ansatz können wir behaupten, dass Sie den @Binding-Eigenschaftswrapper in SwiftUI verwenden können, um eine Verbindung zu einem an anderer Stelle gespeicherten Status herzustellen. Ein Beispiel ist @State, dessen Bindung Sie an eine mit @Binding gekennzeichnete Eigenschaft übergeben können. Dies ist ein häufiger (wenn nicht der häufigste) Anwendungsfall für Bindungen in SwiftUI!
Bevor wir fortfahren, besprechen wir Bindungen, das Konzept, im Vergleich zu @Binding, dem Eigenschaften-Wrapper. Da es sich bei beiden um ähnliche, aber unterschiedliche Konzepte handelt, ist es wichtig, dass wir uns über diesen Teil des Spielfelds im Klaren sind, bevor wir weitermachen.
Bindungen
Eine Bindung ist, wie der Name schon sagt, eine Verbindung zwischen zwei Dingen. In SwiftUI befindet sich eine Bindung zwischen einer Eigenschaft, die Daten speichert, und einer Ansicht, die diese Daten anzeigt und ändert.
Bindungen waren schon lange vor der Einführung durch SwiftUI (und iOS) Teil der reaktiven Programmierung. React, das JavaScript-Framework, verwendet Bindungen, ebenso wie Angular zuvor. Tatsächlich verwenden Ereignis-Listener, Beobachter und sogar das gute alte Notification Center eine Form der Verbindung oder „Bindung“, um den Datenfluss zu bewältigen.
Die Arbeit mit Bindungen in SwiftUI ist sehr übersichtlich – es braucht nur ein $-Zeichen – und das ist ein Vorteil gegenüber weniger modernen Ansätzen wie dem Notification Center. Bindungen sind fast magisch: Sie verbinden eine Sache mit der anderen und bei Änderungen fließen Daten zwischen ihnen.
@Bindung
Das Schlüsselwort @Binding in SwiftUI ist ein Eigenschaften-Wrapper, was bedeutet, dass es eine Eigenschaft mit einigen zusätzlichen Funktionen umschließt. Im Fall von @Binding wird ein Wert vom Typ „Binding“ synthetisiert, wobei „Value“ der Typ der Daten ist, die Sie binden.
Beispielsweise erstellt @Binding var isPlaying: Bool eine Bindung vom Typ Binding. Dieser Wert vom Typ Binding, die Bindung „selbst“, kann über den projizierten Wert der Eigenschaft mit $isPlaying weitergegeben werden. Dadurch können Sie den Wert der Eigenschaft mit isPlaying lesen/schreiben und mit $isPlaying eine Bindung daran weitergeben.
Es ist schwierig, Eigenschaftswrapper und -bindungen als separate Konzepte zu betrachten, da sie so eng miteinander verbunden sind. Es ist gut zu wissen, dass Eigenschafts-Wrapper wie @ObservedObject auch Bindungen offenlegen, was bedeutet, dass einige Eigenschafts-Wrapper eine Bindung verwenden/offenlegen, während andere etwas anderes offenlegen.
Schauen wir uns ein Beispiel an. Zuerst erstellen wir eine CountButton-Ansicht:
struct CountButton: Ansicht{@Binding-Variablenanzahl: Int
Var-Körper: einige Ansicht {Button(“\(count) times”) {zählen += 1}}}
Als nächstes erstellen wir eine CounterView:
struct CounterView: Ansicht{@State private Variable SwiftCount: Int = 0
Var-Körper: einige Ansicht {Text („Times entdeckte einen lila-goldgestreiften Mauersegler:“)CountButton(count: $swiftCount)}}
Lassen Sie uns aufschlüsseln, was in diesem Code vor sich geht. Schauen Sie sich zunächst an, wie wir zwei Eigenschaften-Wrapper verwenden:
- @State private var SwiftCount: Int = 0 verfolgt die Anzahl in CounterView
- @Binding var count: Int ist eine Bindung an einen an anderer Stelle gespeicherten Int
Wir haben 2 Ansichten in diesem Code. Die Ansicht der obersten Ebene ist CounterView, die über eine Textansicht mit einem einfachen Zeichenfolgewert verfügt. Die CounterView enthält auch CountButton als Unteransicht.
Innerhalb von CountButton sehen Sie nur eine Button-Unteransicht. Diese Schaltfläche hat 2 Parameter:
- „\(count) times“, der Text auf der Schaltfläche, unter Verwendung des Werts von count
- { count += 1 }, die Aktion (ein Abschluss), die ausgeführt wird, wenn Sie darauf tippen
Sie haben es wahrscheinlich bereits erraten, aber diese Ansichten arbeiten zusammen, um den Zählwert zu erhöhen, wenn Sie auf die Schaltfläche tippen. Eeeeasy!
Das Magische an dem Code ist, wie @State und @Binding zusammenarbeiten. Sehen Sie, innerhalb von CountButton benötigen wir Zugriff auf die Eigenschaft „swiftCount“ in CounterView, da wir dort ihren Wert anzeigen und ändern.
Wir wollen die Zählung nur einmal speichern – eine einzige Quelle der Wahrheit – also brauchen wir eine Möglichkeit, sie an CountButton zu übergeben. Hier kommt die Bindung ins Spiel.
Innerhalb von CountButton ist die count-Eigenschaft mit @Binding markiert. Dadurch wird die Ansicht angewiesen, sich selbst zu aktualisieren, wenn sich der Wert von count ändert. Die @Binding-Eigenschaft speichert jedoch keinen eigenen Wert. Der Zählstand wird an anderer Stelle gespeichert. Wo, fragen Sie? In der CountView-Struktur mit der Eigenschaft „swiftCount“!
Da die count-Eigenschaft in CountButton mit @Binding markiert ist und keinen Standardwert hat, können wir eine Bindung an den CountButton(count:)-Initialisierer übergeben. Der Typ seines Zählparameters ist Binding†.
Der @State-Eigenschaftswrapper stellt eine Bindung an seinen zugrunde liegenden Wert bereit, auf den wir über das Präfix $ zugreifen können. Für eine @State-Eigenschaft mit dem Namen „swiftCount“ lautet der Name der Bindung $swiftCount. Dies ist praktisch eine zweite Eigenschaft, die aufgrund von @State synthetisiert wird.
Kurz gesagt handelt es sich hierbei um eine ausgeklügelte und komplizierte Möglichkeit, einige Daten an die CountButton-Ansicht zu übergeben. Sie können jedoch sehen, wie nützlich Bindungen und @Binding sind, wenn die Daten, mit denen Sie arbeiten, komplexer sind. Sie verwenden @Binding, um eine Bindung an einen Zustand zu erstellen/zu übergeben, der außerhalb der Ansicht erstellt wird.
†: Note dass der Typ der count-Eigenschaft Int ist, der Typ von $count und $swiftCount Binding ist und der Typ des count-Parameters für den Initialisierer von CountButton Binding ist. Normalerweise hat der Memberwise-Initialisierer dieselben Typen wie die Eigenschaften, auf denen er basiert – aber nicht für @Binding!
Note: Es ist wichtig zu beachten, dass @Binding eine Ansicht von ihrem Status abhängig macht, @Binding jedoch nicht seinen eigenen Status speichert. Es kommt immer von woanders, etwa von @State oder @ObservedObject. Eine gute Erinnerung an diese Entwurfsentscheidung ist, dass mit @State gekennzeichnete Eigenschaften im Allgemeinen privat gemacht werden („lokaler Status“) und mit @Binding gekennzeichnete Eigenschaften nicht mit einem Standardwert deklariert werden.
Schauen wir uns ein weiteres Beispiel von @Binding an. In diesem Fall sind die Prinzipien dieselben wie zuvor – der Code ist jedoch völlig anders.
Schauen Sie sich das an:
Struktur LivingRoom: Ansicht{@State private Variable LightsOn = false
Var-Körper: einige Ansicht {Text(lightsOn ? „Lichter an! ?“ : „Lichter aus?“)Toggle(“Lichter steuern”, isOn: $lightsOn)}}
Was ist denn hier los? Wir haben wieder diesen @State-Eigenschaftswrapper für die Eigenschaft LightsOn. Basierend auf dem, was wir zuvor besprochen haben, wissen Sie, dass die LivingRoom-Ansicht jetzt vom Status des LightsOn-Booleschen Werts abhängt.
In der Textansicht überprüfen wir den Wert von LightsOn mit dem ternären Bedingungsoperator ?: und zeigen ein wenig Text an:
- Wenn LightsOn wahr ist, wird „Lights on!“ angezeigt. ?“
- Wenn LightsOn falsch ist, wird „Lichter aus?“ angezeigt.
Wenn sich der Wert von LightsOn ändert, aktualisiert sich die Ansicht selbst. Dies ist ein Kernprinzip von SwiftUI. state steuert die Benutzeroberfläche. Nur weil wir @State für diese Eigenschaft verwenden!
Was ist mit Toggle und dieser Bindung? Sehen Sie, wir übergeben $lightsOn für seinen isOn-Parameter an die Toggle-Ansicht. Dadurch wird die Ein-Aus-Funktionalität des Toggle an den Wert für LightsOn gebunden – „on“ ist wahr und „off“ ist falsch.
Interessant ist, dass dieser Ansatz fast genau dem entspricht, was wir im vorherigen Abschnitt mit @Binding codiert haben. Da fragen Sie sich: Verwendet die Toggle-Ansicht intern @Binding, um eine Bindung als eine ihrer Eigenschaften zu akzeptieren? Darauf können Sie wetten! Wenn wir die Dokumentation durchsehen, sehen wir, dass der Typ von isOn „Binding“ ist. Und das ist genau die Art von $lightsOn!
Sie werden feststellen, dass viele UI-Komponenten in SwiftUI Bindungen als Parameter akzeptieren, da dies der De-facto-Ansatz ist, Ansichten, die Daten anzeigen/ändern, mit den Eigenschaften zu verbinden, die sie speichern.
Einige Beispiele für UI-Elemente, die Bindungen verwenden:
- TextField, SecureField und TextEditor akzeptieren eine String-Bindung für Text
- NavigationLink akzeptiert eine Bool-Bindung für isActive, um die Benutzeroberfläche programmgesteuert anzuzeigen/zu navigieren
- Toggle akzeptiert eine Bool-Bindung für isOn für den Status des Umschalters
- Picker akzeptiert eine Bindung zur Auswahl und gibt den aktuell ausgewählten Wert an
- DatePicker akzeptiert eine Bindung vom Typ Date zur Auswahl, also das aktuell ausgewählte Datum
- Slider akzeptiert eine Bindung für den Wert (Gleitkomma), also den Wert auf dem Slider
- Stepper akzeptiert eine Bindung für den Wert, d. h. den Wert, der erhöht/verringert wird (verwendet Strideable, das ist also ziemlich flexibel!)
- ColorPicker akzeptiert eine Bindung vom Typ Farbe zur Auswahl, also die Farbe, die im Picker ausgewählt ist
Sie können natürlich auch eigene UI-Elemente (oder andere Komponenten) erstellen, die Bindungen und @Binding verwenden. Das ist der springende Punkt!
Wie geben Sie Daten und Bindungen in Ihrem Code weiter? Erfahren Sie mehr in diesem Tutorial: Anleitung: Übergeben von Daten zwischen Ansichten mit SwiftUI
Bisher haben wir uns angeschaut, was Bindungen sind und wie Sie den @Binding-Eigenschaftswrapper in Ihrem Code verwenden. Wir haben uns auch UI-Komponenten angesehen, die Bindungen verwenden, und wie Sie eine $name-Bindung vom @State-Eigenschaftswrapper erhalten können.
Schauen wir uns den letzten Teil genauer an. Immer wenn Sie mit dem @State-Eigenschaftswrapper einen Status erstellen, erhalten Sie auch kostenlos eine Bindung. Mit dieser Bindung können Sie ein UI-Element, z. B. ein TextField, mit der Eigenschaft verbinden. Wenn sich der Text im Textfeld ändert, ändert sich auch der Wert in der Eigenschaft – und umgekehrt.
Schauen Sie sich dieses kurze Beispiel an:
struct BookEdit: Ansicht{@State privater Variablentitel: String = „“Autor der privaten @State-Variable: String = „“
Var-Körper: einige Ansicht {TextField(„Titel“, Text: $title)TextField(„Autor“, Text: $author)
Text („\(Titel) von \(Autor)“)}}
Im obigen Code haben wir einen Eigenschaftstitel vom Typ String erstellt. Es wird von @State verpackt. Im TextField-Initialisierer übergeben wir $title als seinen Textparameter. Dies ist eine Bindung an die Texteigenschaft des Textfelds.
Wenn wir etwas in das Textfeld eingeben, ändert sich auch der Wert des Titels. Diese Änderung wird in der Textansicht unten angezeigt, wenn auch etwas deutlich. Die Bindung erfolgt in zwei Richtungen: Das Textfeld ändert den Textwert und alle Textänderungen werden auch im TextField angezeigt.
Note dass der Name der Bindung $title ist, also der Name der umschlossenen Eigenschaft plus ein $-Zeichen. Dies ist der projizierte Wert des Eigenschaften-Wrappers. Viele Eigenschaften-Wrapper haben einen projizierten (und verpackten) Wert; Nicht nicht der projizierte Wert jedes Eigenschaftswrappers ist eine Bindung!
Es ist klar, dass man, um mit einer Bindung arbeiten zu können, irgendwo eine Bindung besorgen muss. Welche Eigenschaften-Wrapper stellen Bindungen bereit?
Hier ein kurzer Überblick:
- @Binding – stellt die Bindung an Eigenschaften bereit, besitzt keinen eigenen Status, verwendbar für Werttypen (und Eigenschaften von Referenztypen, die Werttypen sind!)
- @State – stellt die Bindung an die Eigenschaft bereit, besitzt den Status und wird für Werttypen verwendet
- @ObservedObject – stellt Bindungen zu Eigenschaften eines beobachteten Objekts bereit, besitzt keinen eigenen Status, wird an anderer Stelle initialisiert und für Referenztypen verwendet
- @EnvironmentObject – stellt Bindungen zu Eigenschaften eines Umgebungsobjekts bereit, besitzt keinen eigenen Status, wird an anderer Stelle initialisiert und über EnvironmentObject(_:) übergeben.
- @StateObject – stellt Bindungen zu Eigenschaften eines Statusobjekts bereit, besitzt den Status, wird lokal initialisiert und für Referenztypen verwendet
- Es gibt andere: @SceneStorage, @GestureState, @FocusedValue und @FocusedBinding
Welchen dieser Eigenschafts-Wrapper Sie am Ende verwenden werden, hängt weitgehend davon ab, welchen spezifischen Eigenschafts-Wrapper Sie benötigen. Es ist jedoch gut zu wissen, dass sie Bindungen für ihre Eigenschaften bereitstellen.
Eine gute Faustregel für jetzt und in Zukunft ist, dass ein Eigenschaften-Wrapper, der den Zustand bereitstellt/verwaltet, wahrscheinlich auch eine Bindung hat. Es macht Sinn: Sie benötigen einen Status, um einen Wert zu lesen/schreiben, und Sie können die Bindung verwenden, um ein UI-Element an diesen Status zu binden.
Suchen Sie nach einem ausführlichen Tutorial zu einem ObserableObject, das Bindungen verwendet? Schauen Sie sich das an: @ObservedObject und Freunde in SwiftUI
Das Kernprinzip einer Bindung besteht darin, dass sie einen Wert mit einem UI-Element oder einer Ansicht verbindet, die diesen Wert anzeigt und ändert. Die Bindung ist bidirektional, was bedeutet, dass das UI-Element den Wert ändern kann und der Wert das UI-Element ändern kann. Dies bedeutet auch, dass die Ansicht bei Verwendung von @Binding von ihrem Status abhängig wird. Sie können Ihre eigenen Bindungen erstellen oder sie von Eigenschaften-Wrappern wie @State und ObservedObject abrufen. Eindrucksvoll!
