SwiftUI pozwala widokowi na pobranie informacji o urządzeniu. Na przykład: jaki motyw graficzny ma ustawiony na swoim telefonie: jasny czy ciemny? Jakiego język jest ustawiony na urządzeniu?
Pozyskując takie informacje, możesz wykorzystać je w swojej aplikacji.
Za pobranie informacji systemowych odpowiedzialny jest wrapper: @Environment.
Pełną listę informacji, jakie możesz pobrać znajdziesz w EnvironmentValues
W tym przykładzie za pomocą @Environment pobierana jest wartość z colorScheme, która zawiera informację, jaki motyw graficzny jest ustawiony w urządzeniu.
Na podstawie tego stylizowany jest odpowiednio przycisk.
Aplikacja napisana pod iOS może być w następującym stanie:
Non-running
Ten stan oznacza, że aplikacja nie jest uruchomiona.
Inactive
Oznacza, ze aplikacja jest uruchomiona na pierwszym planie (ang.: foreground), ale użytkownik nie ma z nią interakcji. Aplikacja w takim stanie nie odbiera zdarzeń.
Program wchodzi w ten stan na krótko przed przejściem do innego stanu na przykład w tryb background.
Przykład:
Użytkownika używa aplikacji i chce się przełączyć do innego programu — aplikacja najpierw wchodzi w tryb inactive, a następnie background.
Active
Aplikacja działa na pierwszym planie, użytkownik ma z nią interakcję i odbiera ona zdarzenia.
Background
Aplikacja jest wprawdzie uruchomiona, ale jest "schowana" - działa w tle. Użytkownik nie ma z nią interakcji. Kod aplikacji jest wykonywany.
W ten stan aplikacja przechodzi na przykład, gdy użytkownik przełącza się na inny program.
Suspended
Aplikacja jest uruchomiona w tle, ale jej kod nie jest wykonywany.
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.