@State е Property Wrapper в SwiftUI за управление на локално състояние в рамките на един изглед. SwiftUI автоматично прерисува изгледа при всяка промяна на @State свойството, правейки интерфейса реактивен без ръчни извиквания за обновяване. Според Apple Developer Documentation (2025), @State се препоръчва за прости типове и структури, принадлежащи на един изглед. @State е най-лесният начин за добавяне на интерактивност към SwiftUI интерфейс.
Основни точки
@State е вграден Property Wrapper в SwiftUI, който позволява на изглед да съхранява и проследява собствено състояние. Когато стойността на @State се промени, SwiftUI автоматично прерисува изгледа, като извиква отново свойството body. Това е основата на реактивното програмиране в SwiftUI: разработчикът декларира състоянието, а рамката поема синхронизацията на интерфейса.
@State създава област за съхранение в купа, управлявана от SwiftUI. Тази област е персистентна — тя оцелява при многократни инициализации на структурата на изгледа, които се случват при всяко рендиране. SwiftUI използва идентификатора на изгледа (генериран на база позиция в йерархията) за свързване на @State свойството с конкретен изглед. Благодарение на това състоянието не се нулира при обновяване на родителския изглед.
Важно ограничение: @State е предназначен само за стойностни типове (структури, изброявания, примитиви). За референтни типове (класове) използвайте @StateObject или @ObservedObject. Ако присвоите клас на @State свойство, SwiftUI няма да може да открие промени вътре в обекта — само замяна на цялата референция.
SwiftUI имплементира @State чрез вътрешния механизъм Storage. Всяко @State свойство получава специална клетка памет, съхранявана в специалния storage контейнер на изгледа. Когато се осъществи запис в wrappedValue, SwiftUI чрез didSet уведомява своя граф на зависимостите (dependency graph) за необходимостта от прерисуване.
struct ContentView: View {
@State private var name: String = "User"
@State private var isLoggedIn: Bool = false
var body: some View {
VStack {
Text("Здравейте, \(name)")
Button(isLoggedIn ? "Изход" : "Вход") {
isLoggedIn.toggle()
}
}
}
}
В примера има две @State свойства: name (String) и isLoggedIn (Bool). При извикване на isLoggedIn.toggle() SwiftUI маркира ContentView като изискващ обновяване и рестартира body в следващия цикъл на рендиране. Ключов момент: @State свойствата винаги се декларират с модификатор private — това е сигнал, че състоянието принадлежи изключително на текущия изглед и не трябва да се променя отвън директно.
За наблюдение на промените SwiftUI използва CurrentValueSubject от Combine. Всяко @State свойство създава скрит издател, който уведомява системата при всяка промяна. Това позволява на SwiftUI да прерисува само минимално необходимия набор от изгледи, избягвайки пълно обновяване на йерархията.
@State е оптимален за прости локални състояния: текстови полета за търсене, булеви флагове за модални прозорци, превключватели за настройки, броячи, избрани елементи от списък. Ако стойността се използва само в един изглед и неговите вложени компоненти (чрез @Binding), @State е правилният избор. За състояния, които трябва да оцелеят при затваряне на изгледа (напр. данни от формуляр), @State също е подходящ, докато изгледът остава в йерархията.
Не използвайте @State за глобални състояния на приложението, кеширане на мрежови данни или обекти, използвани на няколко екрана. За тези цели са предназначени @StateObject и @EnvironmentObject. @State не е подходящ и за съхраняване на големи обеми данни — всеки път при промяна целият изглед ще бъде прерисуван.
@Binding е мост между @State в родителския изглед и дъщерния изглед, който трябва да променя това състояние. Родителят декларира @State, а дъщерният компонент получава Binding чрез проекцията $. Промяната на Binding в дъщерния изглед автоматично обновява @State в родителя — и обратно. Това осигурява еднопосочен поток от данни с възможност за обратна връзка.
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)
}
}
В листинга ParentView притежава @State text, а ChildView получава $text като Binding. TextField вътре в ChildView се свързва с това Binding чрез text: $text. Когато потребителят пише в TextField, стойността се променя в ChildView чрез Binding, което води до обновяване на @State в ParentView. И двата изгледа се прерисуват с новата стойност.
Най-честата грешка — присвояване на клас на @State свойство. Ако напишете @State var model = MyClass(), SwiftUI няма да може да проследи промени на свойства вътре в класа — само замяна на самия обект. За класове винаги използвайте @StateObject. Вторият често срещан проблем — деклариране на @State без модификатор private, което нарушава принципа на капсулиране на състоянието.
Директно предаване на @State на дъщерен изглед без $ — още една типична грешка. Ако предадете TextField(text: text) вместо TextField(text: $text), дъщерният компонент ще получи обикновен низ, а не Binding. Промяната на текст в TextField няма да бъде синхронизирана с родителския @State. Винаги използвайте проекция $ за предаване на Binding.
Третата грешка — множество @State свойства за свързани данни. Ако няколко стойности логически образуват едно цяло (напр. полета на формуляр), обединете ги в една структура с един @State. Това опростява предаването на състояние на дъщерни изгледи и намалява броя на отделните тригери за обновяване.
@State се използва в повечето SwiftUI проекти за основна интерактивност. Нека разгледаме пример за форма за вход, където @State управлява текстовите полета и състоянието на зареждане. Този модел се среща във всяко приложение — от прости бележки до сложни корпоративни решения.
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("Вход") {
login()
}.disabled(isLoading)
}
}
private func login() {
isLoading = true
// Изпълнение на мрежова заявка
}
}
В примера има четири @State свойства: email и password за полетата на формуляра, isLoading за индикация на зареждане и errorMessage за показване на грешки. Всяко свойство независимо управлява своята част от интерфейса. При промяна на isLoading бутонът автоматично се блокира чрез disabled(isLoading) — без ръчно обновяване на UI.
Често задавани въпроси
@State е предназначен за локално състояние на конкретен изглед. Модификаторът private гарантира, че други компоненти не могат да го променят директно, нарушавайки капсулацията. За външен достъп използвайте проекцията $.
Да, @State поддържа масиви и речници, тъй като те са стойностни типове. При промяна на елемент от масив обаче SwiftUI прерисува целия изглед. За големи списъци е по-ефективно да използвате @StateObject с @Published.
@State работи коректно с Optional типове. При присвояване на nil SwiftUI открива промяната и прерисува изгледа. Това е удобно за състояния като errorMessage: String?, където nil означава липса на грешка.
@State запазва стойността, докато изгледът остава в йерархията. Ако изгледът бъде премахнат от йерархията и добавен отново, @State се инициализира наново със стойността по подразбиране. За персистентност използвайте @AppStorage.
Да, обвийте промяната в withAnimation: withAnimation(.easeInOut) { isExpanded.toggle() }. SwiftUI анимира прехода между старото и новото състояние на интерфейса с указания тип анимация.
Обобщение
Ще разработим мобилно приложение под ключ
IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.
Прочетете също