@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 призначений лише для Value-типів (структур, переліків, примітивів). Для посилальних типів (класів) використовуйте @StateObject або @ObservedObject. Якщо присвоїти клас властивості @State, SwiftUI не зможе виявити зміни всередині об'єкта — лише заміну посилання повністю.
SwiftUI реалізує @State через внутрішній механізм Storage. Кожна властивість @State отримує виділену комірку пам'яті, яка зберігається у спеціальному контейнері зберігання представлення. Коли відбувається запис у wrappedValue, SwiftUI через didSet повідомляє свій граф залежностей про необхідність перемалювання.
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 підтримує масиви та словники, оскільки це Value-типи. Однак при зміні елемента масиву SwiftUI перемальовує все представлення. Для великих списків ефективніше використовувати @StateObject з @Published.
@State коректно працює з Optional-типами. При присвоєнні nil SwiftUI виявляє зміну та перемальовує представлення. Це зручно для станів errorMessage: String?, де nil означає відсутність помилки.
@State зберігає значення, поки представлення залишається в ієрархії. Якщо представлення видаляється з ієрархії та додається знову, @State ініціалізується заново зі значенням за замовчуванням. Для персистентності використовуйте @AppStorage.
Так, оберніть зміну в withAnimation: withAnimation(.easeInOut) { isExpanded.toggle() }. SwiftUI анімує перехід між старим і новим станом інтерфейсу з указаним типом анімації.
Підсумки
Ми розробимо мобільний застосунок під ключ
IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.
Читайте також