@Binding — co to jest, jak działa i przykłady w SwiftUI

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

@Binding to Property Wrapper w SwiftUI, który tworzy referencję do danych należących do innego komponentu. Binding nie przechowuje wartości samodzielnie — jedynie udostępnia dostęp do istniejącego źródła prawdy poprzez projekcję $. Według Apple Developer Documentation (2025), Binding zapewnia reaktywne dwukierunkowe połączenie między widokiem nadrzędnym a podrzędnym bez bezpośredniego posiadania danych. @Binding to kluczowy mechanizm przekazywania zmiennego stanu w dół hierarchii.

Najważniejsze

  • @Binding — Property Wrapper do tworzenia referencji do stanu widoku nadrzędnego
  • Bez posiadania — Binding nie przechowuje danych, a jedynie udostępnia dostęp do źródła prawdy
  • Projekcja $ — $stateValue tworzy Binding z @State lub @StateObject
  • Dwukierunkowe połączenie — zmiany w widoku podrzędnym natychmiast odzwierciedlają się w nadrzędnym
  • Binding.constant — stała wartość do prototypowania bez sprzężenia zwrotnego

Czym jest @Binding w SwiftUI?

@Binding to Property Wrapper, który tworzy dwukierunkowe połączenie między właściwością przechowywaną w widoku nadrzędnym a komponentem podrzędnym. Główna różnica między Binding a @State: Binding nie posiada danych. On jedynie odczytuje i zapisuje wartość przez prawdziwe źródło — @State, @StateObject lub inny Binding w rodzicu. Bez Binding widoki podrzędne nie mogłyby modyfikować stanu przodka bez callbacków lub delegatów.

Binding jest zaimplementowany jako struktura z dwiema właściwościami: wrappedValue (bieżąca wartość) i projectedValue (sam Binding, dostępny przez $). Gdy widok podrzędny zmienia wrappedValue przez Binding, SwiftUI przekazuje zmianę do źródła danych i przerysowuje wszystkie zależne widoki. Dzieje się to synchronicznie w bieżącym cyklu aktualizacji.

Ważna cecha: @Binding nie ogranicza się do jednopoziomowego przekazywania. Binding można przekazywać przez wiele poziomów hierarchii — każdy komponent podrzędny otrzymuje referencję do tego samego źródła danych. Zmiana na dowolnym poziomie powoduje jednolitą aktualizację wszystkich powiązanych widoków.

Jak działa dwukierunkowe połączenie Binding

Mechanizm dwukierunkowego połączenia przez @Binding opiera się na projekcji Property Wrapperów. Gdy rodzic deklaruje @State var value: T, SwiftUI automatycznie generuje projekcję $value typu Binding. Przekazując $value do komponentu podrzędnego z @Binding var value: T, łączysz oba widoki z jedną komórką pamięci. Każdy zapis przez Binding w widoku podrzędnym powoduje przerysowanie obu komponentów.

swift
struct SliderContainer: View {
    @State private var value: Double = 0.5

    var body: some View {
        VStack {
            Text("Wartość: \(value)")
            SliderView(value: $value)
        }
    }
}

struct SliderView: View {
    @Binding var value: Double

    var body: some View {
        Slider(value: $value, in: 0...1)
    }
}

W przykładzie SliderContainer posiada @State value, a SliderView otrzymuje Binding przez $value. Slider wewnątrz SliderView jest powiązany z tym Bindingiem. Podczas przeciągania suwaka Slider zmienia wartość przez Binding, co automatycznie aktualizuje @State w SliderContainer i oba widoki wyświetlają aktualną liczbę. Cały łańcuch działa bez ani jednego callbacka czy powiadomienia.

Do tworzenia Binding z @StateObject lub @ObservedObject używa się tej samej projekcji: $object.property daje Binding. Pozwala to przekazywać poszczególne właściwości ObservableObject do widoków podrzędnych bez przekazywania całego obiektu. Takie podejście daje węższe powiązanie i zapobiega zbędnym przerysowaniom.

@Binding vs callbacki: co wybrać

Przed SwiftUI standardowym sposobem przekazywania zmian w górę hierarchii były callbacki i delegaty: rodzic przekazywał domknięcie, komponent podrzędny wywoływał je przy zmianie. @Binding oferuje alternatywę z mniejszą ilością kodu i bardziej deklaratywnym składem. Zamiast przekazywać domknięcie completion wystarczy przekazać $stateValue.

Kryterium@BindingCallbacki
KodJedna adnotacja + $Domknięcie + wywołanie
WielopoziomowośćAutomatycznaŁańcuch domknięć
TestowanieBinding(value:constant)Mock-domknięcia
CzytelnośćWysokaŚrednia
ElastycznośćTylko daneDowolna logika

Używaj @Binding, gdy widok podrzędny potrzebuje tylko odczytywać i zmieniać wartość. Jeśli wymagane jest wykonanie dodatkowych działań przy zmianie (walidacja, logowanie, żądanie sieciowe), łącz Binding z callbackiem: przekazuj Binding dla danych i domknięcie dla zdarzeń. Na przykład TextField może powiązać się z Bindingiem, a onChange uruchomi walidację.

Wzorce użycia @Binding w projektach

@Binding jest stosowany w kilku typowych scenariuszach. Pierwszy — niestandardowe kontrolki: przełączniki, suwaki, wybory kolorów i inne interaktywne elementy przyjmują Binding do dwukierunkowej synchronizacji. Drugi — okna modalne: flaga pokazania sheet jest przekazywana jako Binding, umożliwiając widokowi podrzędnemu zamknięcie się przez presentationMode lub bezpośrednie ustawienie.

Trzeci wzorzec — formularze z podziałem. Jeśli formularz składa się z wielu pól, każde pole można wydzielić do osobnego komponentu przyjmującego Binding dla swojej wartości. Upraszcza to testowanie i ponowne użycie pól między różnymi formularzami. Komponent nadrzędny pozostaje jedynym właścicielem całego modelu formularza.

swift
struct FormField: View {
    let title: String
    @Binding var text: String

    var body: some View {
        VStack(alignment: .leading) {
            Text(title).font(.caption)
            TextField("Enter \(title.lowercased())", text: $text)
                .textFieldStyle(.roundedBorder)
        }
    }
}

Komponent FormField przyjmuje etykietę i Binding do łańcucha. Wyświetla podpis i TextField powiązany z przekazanym Bindingiem. Dowolny formularz może używać FormField wielokrotnie, przekazując $property dla każdego pola. Zmniejsza to powielanie znaczników i centralizuje stylizację pól tekstowych.

Tworzenie niestandardowych Binding

SwiftUI pozwala tworzyć Binding ręcznie przez inicjalizator Binding(get:set:). Jest to przydatne, gdy trzeba dodać logikę przy odczycie lub zapisie wartości. Na przykład można utworzyć Binding, który formatuje liczbę przed zapisaniem, lub Binding synchronizujący wartość z zdalnym serwerem przy każdej zmianie.

swift
struct ValidatedField: View {
    @State private var email: String = ""

    var emailBinding: Binding<String> {
        .init(
            get: { email },
            set: { email = $0.lowercased().trimmingCharacters(in: .whitespaces) }
        )
    }

    var body: some View {
        TextField("Email", text: emailBinding)
    }
}

W listingu niestandardowy emailBinding automatycznie przekształca tekst na małe litery i usuwa spacje przy każdej zmianie. TextField używa tego Bindinga zamiast bezpośredniego powiązania z $email. Takie podejście pozwala scentralizować walidację i transformację danych wewnątrz Bindinga, nie zaśmiecając kodu handlerami onChange.

Błędy i antywzorce z @Binding

Pierwszy i najczęstszy błąd — przekazanie wartości zamiast Bindinga. Jeśli komponent podrzędny deklaruje @Binding var text: String, a rodzic przekazuje text (bez $), kompilator zgłosi błąd: Cannot convert value of type 'String' to expected argument type 'Binding'. Rozwiązanie — zawsze używaj prefiksu $ przy przekazywaniu: $text.

Drugi błąd — Binding do danych tylko do odczytu. Jeśli widok podrzędny potrzebuje tylko odczytywać wartość, nie używaj @Binding — wystarczy zwykłe let lub @State z rodzica. Binding implikuje możliwość zapisu, a zbędne uprawnienia do modyfikacji utrudniają debugowanie i naruszają zasadę minimalnych uprawnień.

Trzeci problem — Binding.constant w produkcji. Binding.constant(value) tworzy fikcyjne powiązanie bez sprzężenia zwrotnego — zmiany są ignorowane. Używaj constant tylko do prototypowania i podglądów (Xcode Previews), ale nigdy w rzeczywistym kodzie. Do testów używaj Binding(get:set:) z kontrolowanym zachowaniem.

Często zadawane pytania

Jaka jest różnica między @Binding a @State?

@State posiada dane i zarządza ich przechowywaniem na stercie. @Binding tylko odwołuje się do istniejącego stanu bez posiadania. @State jest zawsze private, @Binding — parametrem wejściowym widoku podrzędnego.

Czy można utworzyć Binding bez @State?

Tak, przez inicjalizator Binding(get:set:) lub Binding.constant(value). Binding można również uzyskać z @StateObject przez projekcję $object.$property oraz z Publisher przez Binding(get:set:) wewnątrz Subscribe.

Jak przekazać Binding przez kilka poziomów zagnieżdżenia?

@Binding jest przekazywany przez łańcuch: każdy pośredni komponent deklaruje @Binding i przekazuje go dalej przez $. Wszystkie poziomy odwołują się do jednego źródła danych w głównym widoku.

Dlaczego Binding.constant nie aktualizuje interfejsu?

Binding.constant tworzy niemą otoczkę — setter ignoruje nowe wartości. Jest przeznaczony tylko do prototypowania i SwiftUI Previews, gdzie nie jest wymagane sprzężenie zwrotne od komponentu podrzędnego.

Czy @Binding może być opcjonalny?

Tak, Binding<T?> jest obsługiwany. Jeśli przekażesz Binding<String?>, widok podrzędny będzie mógł ustawić nil. Jest to wygodne dla opcjonalnych pól formularzy lub stanów z możliwością resetowania.

Podsumowanie

  • @Binding — Property Wrapper do dwukierunkowego połączenia z danymi widoku nadrzędnego
  • Nie posiada danych — tylko udostępnia dostęp do źródła prawdy
  • Projekcja $ przekształca @State, @StateObject w Binding do przekazania widokom podrzędnym
  • Niestandardowy Binding tworzy się przez Binding(get:set:) z dodatkową logiką
  • Binding.constant — tylko do podglądów i prototypów
  • Przekazywanie wielopoziomowe — Binding przechodzi przez dowolną głębokość zagnieżdżenia
  • Alternatywa dla callbacków — deklaratywny sposób modyfikacji stanu z komponentów podrzędnych

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ż