Różnica między nimi jest taka, że @ObservedObject powinieneś użyć, w przypadku wymiany danych między widokami. Kiedy chcesz przekazać z jednego widoku typ referencyjny do innego.
Przykłady
Przykład 1
Model:
classCounter: ObservableObject{
@Publishedvar value: Int = 0funcincrement() {
value += 1
}
funcdecrement() {
value -= 1
}
funcreset() {
value = 0
}
}
Ponieważ @ObservedObject jest bardzo podobne do @StateObject często są ze sobą mylone. Niepoprawne użycie @ObservedObject może powodować nieoczekiwane wyniki.
Widok ContentView zawiera CounterView nic do niego nie jest przekazywane.
Jeśli użytkownik zmieni wartość licznika, a następnie wpisze jakiś tekst w pole tekstowe, to licznik... zostanie zresetowany do 0.
Dlaczego tak się dzieje?
Po pierwsze widok ContentView zawiera zmienną text. Jeśli wartość tej zmiennej zostanie zmieniona, stan widoku także ulegnie zmianie. To znaczy, że widok zostanie przerysowany.
Przerysowanie powoduje, że dla zmiennej oznaczonej jako @ObservedObject (w tym przypadku w widoku CounterView) poprzednia instancja klasy Counter zostanie zniszczona i stworzona nowa. To właśnie powoduje ten reset.
Można temu zaradzić. Wystarczy, że zamienisz w widoku CounterView linijkę:
@ObservedObjectvar counter = Counter()
na:
@StateObjectvar counter = Counter()
lub zaimplementujesz, w taki sposób, jak pokazane jest w pierwszym przykładzie.
Wystarczy trzymać się zasady: jeśli potrzebujesz przekazać do jakiegoś widoku typ referencyjny (na przykład obiekt klasy) użyj @ObservedObject.
Klasy w Swift są typami referencyjnymi. Jeśli potrzebujesz w swoim widoku mieć właściwość, która jest obiektem klasy powinieneś oznaczyć go jako @StateObject.
Właściwość oznaczona jako @StateObject sprawia, że ten obiekt będzie obserwowany. A właściwie to stan obiektu. Kiedy stan obiektu się zmieni, widok zostanie zaktualizowany. Dodatkowo używając tego wrappera, informujesz w ten sposób, że widok jest właścicielem tego obiektu.
Używając @StateObject masz pewność obiekt nie zostanie zniszczony, kiedy widok jest aktualizowany.
Obiekt, który jest oznaczony jako @StateObjectmusi być zgodny z protokołemObservableObject.
classMyClass: ObservableObject{
...
}
Dzięki temu informujesz SwiftUI o ewentualnej zmianie stanu obiektu tej klasy w wyniku czego widok zostanie przerysowany.
To jeszcze nie wszystko. Potrzebujesz oznaczyć właściwość klasy jako @Published:
classMyClass: ObservableObject{
@Publishedvar value = 0
}
Za pomocą @Published wskazujesz konkretnie, które właściwości klasy mają być monitorowane.
Przykład
Model:
classCounter: ObservableObject{
@Publishedvar value: Intinit(value: Int) {
self.value = value
}
funcincrement() {
value += 1
}
funcdecrement() {
value -= 1
}
funcreset() {
value = 0
}
}
Patrząc na powyższy przykład, załóżmy, że potrzebujesz w widoku ContentView stworzyć instancję klasy Counter w funkcji initContentView. Bo na przykład nie wiesz, jaka ma być wartość początkowa.
W SwiftUI możesz łatwo tworzyć aplikacje za pomocą kilku widoków. Jeden widok może składać się z kilku innych.
W niektórych przypadkach będzie potrzeba dzielenia się danymi między widokiem: rodzic a widokiem: dziecko. Ale to nie wszystko. Może być taka potrzeba, że wartość przekazana między widokami powinna być modyfikowana przez oba widoki, a ewentualne zmiany, powinny być widoczne w obu widokach.
Można to ująć krócej: @Binding pozwala na stworzenie komunikacji dwukierunkowej między widokami.
Użycie @Binding idzie w parze z użyciem @State. @State używasz w widoku rodzica, a @Binding w widoku dziecka.
W SwiftUI widoki są tworzone za pomocą struktur. Struktury w Swift domyślnie są niezmienne (ang.: immutable). Dzięki @State możesz określić, która właściwość będzie mogła zmienić stan widoku.
Kiedy oznaczysz jakąś właściwość jako @State, informujesz w ten sposób SwiftUI, że ma ona być nasłuchiwana i przy każdej zmianie wartości widok ma być odświeżony.
@State powinno się używać dla typów prostych jak: stringi, liczby lub tablice.