@State — co to jest, przeznaczenie i użycie w SwiftUI

Autor: IT Sectr Opublikowano: 2026-06-19 Czas czytania: 7 min

@State to Property Wrapper w SwiftUI do zarządzania stanem lokalnym w obrębie jednego widoku. SwiftUI automatycznie przerysowuje widok przy każdej zmianie właściwości @State, czyniąc interfejs reaktywnym bez ręcznego wywoływania aktualizacji. Według Apple Developer Documentation (2025), @State jest zalecany dla prostych typów i struktur należących do jednego widoku. @State to najprostszy sposób na dodanie interaktywności do interfejsu SwiftUI.

Najważniejsze

  • @State — Property Wrapper dla stanu lokalnego należącego do jednego widoku
  • Automatyczna aktualizacja — SwiftUI ponownie uruchamia body przy zmianie właściwości @State
  • Proste typy — @State nadaje się dla String, Int, Bool, enum i struktur
  • Nie przekazywać zagnieżdżonym widokom — do zmiany z komponentów potomnych używaj @Binding
  • private — właściwości @State zawsze deklaruje się z modyfikatorem private

Czym jest @State w SwiftUI?

@State to Property Wrapper wbudowany w SwiftUI, który pozwala widokowi przechowywać i śledzić własny stan. Gdy wartość @State się zmienia, SwiftUI automatycznie przerysowuje widok, ponownie wywołując właściwość body. To podstawa programowania reaktywnego w SwiftUI: programista deklaruje stan, a framework zajmuje się synchronizacją interfejsu.

@State tworzy obszar przechowywania na stercie zarządzany przez SwiftUI. Ten obszar jest trwały — przetrwa wielokrotne inicjalizacje struktury widoku, które mają miejsce przy każdym renderowaniu. SwiftUI używa identyfikatora widoku (wygenerowanego na podstawie pozycji w hierarchii) do powiązania właściwości @State z konkretnym widokiem. Dzięki temu stan nie jest resetowany przy aktualizacji widoku nadrzędnego.

Ważne ograniczenie: @State jest przeznaczony tylko dla typów wartościowych (struktur, wyliczeń, typów prostych). Dla typów referencyjnych (klas) używaj @StateObject lub @ObservedObject. Jeśli przypiszesz klasę do właściwości @State, SwiftUI nie będzie w stanie wykryć zmian wewnątrz obiektu — tylko całkowitą zamianę referencji.

Jak działa @State pod maską?

SwiftUI implementuje @State poprzez wewnętrzny mechanizm Storage. Każda właściwość @State otrzymuje wydzieloną komórkę pamięci przechowywaną w specjalnym kontenerze storage widoku. Gdy następuje zapis do wrappedValue, SwiftUI przez didSet powiadamia swój graf zależności (dependency graph) o konieczności przerysowania.

swift
struct ContentView: View {
    @State private var name: String = "User"
    @State private var isLoggedIn: Bool = false

    var body: some View {
        VStack {
            Text("Witaj, \(name)")
            Button(isLoggedIn ? "Wyloguj" : "Zaloguj") {
                isLoggedIn.toggle()
            }
        }
    }
}

W przykładzie dwie właściwości @State: name (String) i isLoggedIn (Bool). Przy wywołaniu isLoggedIn.toggle() SwiftUI oznacza ContentView jako wymagający aktualizacji i ponownie uruchamia body w następnym cyklu renderowania. Kluczowy punkt: właściwości @State zawsze deklaruje się z modyfikatorem private — to sygnał, że stan należy wyłącznie do bieżącego widoku i nie powinien być zmieniany z zewnątrz bezpośrednio.

Do obserwowania zmian SwiftUI używa CurrentValueSubject z Combine. Każda właściwość @State tworzy ukrytego wydawcę, który powiadamia system przy każdej zmianie. Pozwala to SwiftUI przerysowywać tylko minimalnie niezbędny zestaw widoków, unikając pełnej aktualizacji hierarchii.

Kiedy używać @State w projekcie

@State jest optymalny dla prostych stanów lokalnych: pól tekstowych wyszukiwania, flag boolowskich dla okien modalnych, przełączników ustawień, liczników, wybranych elementów listy. Jeśli wartość jest używana tylko w jednym widoku i jego zagnieżdżonych komponentach (przez @Binding), @State jest właściwym wyborem. Dla stanów, które powinny przetrwać zamknięcie widoku (np. dane formularza), @State również się nadaje, dopóki widok pozostaje w hierarchii.

  • Pola tekstowe — @State do przechowywania wprowadzonego tekstu w TextField
  • Flagi boolowskie — @State do pokazywania/ukrywania okien modalnych i sheet
  • Wybór elementów — @State do śledzenia wybranej zakładki lub wiersza
  • Liczniki — @State dla wartości liczbowych z inkrementacją/dekrementacją
  • Obliczenia pośrednie — @State do buforowania wyników wewnątrz widoku

Nie używaj @State do stanów globalnych aplikacji, buforowania danych sieciowych lub obiektów używanych na wielu ekranach. Do tych celów służą @StateObject i @EnvironmentObject. @State nie nadaje się również do przechowywania dużych ilości danych — przy każdej zmianie będzie przerysowywany cały widok.

@State i @Binding: współpraca

@Binding to most między @State w widoku nadrzędnym a widokiem potomnym, który potrzebuje zmieniać ten stan. Rodzic deklaruje @State, a komponent potomny otrzymuje Binding przez projekcję $. Zmiana Binding w widoku potomnym automatycznie aktualizuje @State w rodzicu — i odwrotnie. Zapewnia to jednokierunkowy przepływ danych z możliwością sprzężenia zwrotnego.

swift
struct ParentView: View {
    @State private var text: String = ""

    var body: some View {
        ChildView(text: $text)
    }
}

struct ChildView: View {
    @Binding var text: String

    var body: some View {
        TextField("Enter text", text: $text)
    }
}

W listingu ParentView jest właścicielem @State text, a ChildView otrzymuje $text jako Binding. TextField wewnątrz ChildView wiąże się z tym Binding przez text: $text. Gdy użytkownik pisze w TextField, wartość zmienia się w ChildView przez Binding, co powoduje aktualizację @State w ParentView. Oba widoki przerysowują się z nową wartością.

Typowe błędy przy pracy z @State

Najczęstszy błąd to przypisanie klasy do właściwości @State. Jeśli napiszesz @State var model = MyClass(), SwiftUI nie będzie w stanie śledzić zmian właściwości wewnątrz klasy — tylko zastąpienie samego obiektu. Dla klas zawsze używaj @StateObject. Drugi powszechny problem to deklarowanie @State bez modyfikatora private, co narusza zasadę enkapsulacji stanu.

Bezpośrednie przekazywanie @State do widoku potomnego bez $ — kolejny typowy błąd. Jeśli przekażesz TextField(text: text) zamiast TextField(text: $text), komponent potomny otrzyma zwykły string, a nie Binding. Zmiana tekstu w TextField nie będzie synchronizowana z nadrzędnym @State. Zawsze używaj projekcji $ do przekazywania Binding.

Trzeci błąd to wielokrotne właściwości @State dla powiązanych danych. Jeśli kilka wartości logicznie stanowi jedną całość (np. pola formularza), połącz je w jedną strukturę z pojedynczym @State. Upraszcza to przekazywanie stanu do widoków potomnych i zmniejsza liczbę oddzielnych wyzwalaczy aktualizacji.

Przykłady użycia @State w SwiftUI

@State jest używany w większości projektów SwiftUI do podstawowej interaktywności. Rozważmy przykład formularza logowania, gdzie @State zarządza polami tekstowymi i stanem ładowania. Taki wzorzec występuje w każdej aplikacji — od prostych notatek po złożone rozwiązania korporacyjne.

swift
struct LoginView: View {
    @State private var email: String = ""
    @State private var password: String = ""
    @State private var isLoading: Bool = false
    @State private var errorMessage: String?

    var body: some View {
        Form {
            TextField("Email", text: $email)
            SecureField("Password", text: $password)
            Button("Zaloguj") {
                login()
            }.disabled(isLoading)
        }
    }

    private func login() {
        isLoading = true
        // Wykonaj żądanie sieciowe
    }
}

W przykładzie cztery właściwości @State: email i password dla pól formularza, isLoading do wskazywania ładowania i errorMessage do wyświetlania błędów. Każda właściwość niezależnie zarządza swoją częścią interfejsu. Przy zmianie isLoading przycisk automatycznie się blokuje przez disabled(isLoading) — bez ręcznej aktualizacji UI.

Często zadawane pytania

Dlaczego @State deklaruje się z private?

@State jest przeznaczony dla stanu lokalnego konkretnego widoku. Modyfikator private gwarantuje, że inne komponenty nie zmienią go bezpośrednio, naruszając enkapsulację. Do zewnętrznego dostępu używaj projekcji $.

Czy @State może zawierać tablicę lub słownik?

Tak, @State obsługuje tablice i słowniki, ponieważ są to typy wartościowe. Jednak przy zmianie elementu tablicy SwiftUI przerysowuje cały widok. Dla dużych list efektywniejsze jest użycie @StateObject z @Published.

Co się stanie przy przypisaniu nil do właściwości @State z typem Optional?

@State poprawnie działa z typami Optional. Przy przypisaniu nil SwiftUI wykrywa zmianę i przerysowuje widok. Jest to wygodne dla stanów takich jak errorMessage: String?, gdzie nil oznacza brak błędu.

Jak @State zachowuje się przy ponownym pojawieniu się widoku?

@State zachowuje wartość, dopóki widok pozostaje w hierarchii. Jeśli widok zostanie usunięty z hierarchii i dodany ponownie, @State inicjalizuje się od nowa z wartością domyślną. Do trwałości używaj @AppStorage.

Czy można animować zmiany @State?

Tak, owiń zmianę w withAnimation: withAnimation(.easeInOut) { isExpanded.toggle() }. SwiftUI animuje przejście między starym a nowym stanem interfejsu z określonym typem animacji.

Podsumowanie

  • @State — Property Wrapper dla stanu lokalnego jednego widoku, automatycznie aktualizujący interfejs
  • Nadaje się dla prostych typów: String, Int, Bool, a także struktur i enum
  • Nie nadaje się dla typów referencyjnych (klas) — używaj @StateObject
  • Zawsze private — stan nie powinien być zmieniany z zewnątrz bezpośrednio
  • Projekcja $ — tworzy Binding do przekazania prawa zmiany widokom potomnym
  • Wiele @State w jednym widoku — normalna praktyka dla niezależnych stanów
  • withAnimation — pozwala animować zmiany właściwości @State

Opracujemy aplikację mobilną pod klucz

IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.

Omów projekt

Przeczytaj również