@State — это Property Wrapper в SwiftUI для управления локальным состоянием внутри одного представления. SwiftUI автоматически перерисовывает view при каждом изменении @State-свойства, делая интерфейс реактивным без ручных вызовов обновления. По данным Apple Developer Documentation (2025), @State рекомендуется для простых типов и структур, принадлежащих одному view. @State — самый простой способ добавить интерактивность в SwiftUI-интерфейс.
Главное
@State — это Property Wrapper, встроенный в SwiftUI, который позволяет view хранить и отслеживать собственное состояние. Когда значение @State изменяется, SwiftUI автоматически перерисовывает view, вызывая свойство body заново. Это основа реактивного программирования в SwiftUI: разработчик объявляет состояние, а фреймворк берёт на себя синхронизацию интерфейса.
@State создаёт область хранения в куче, управляемую SwiftUI. Эта область персистентна — она переживает повторные инициализации структуры view, которые происходят при каждом рендере. SwiftUI использует идентификатор view (сгенерированный на основе позиции в иерархии) для привязки @State-свойства к конкретному представлению. Благодаря этому состояние не сбрасывается при обновлении родительского view.
Важное ограничение: @State предназначен только для Value-типов (структур, перечислений, примитивов). Для ссылочных типов (классов) используйте @StateObject или @ObservedObject. Если присвоить класс @State-свойству, SwiftUI не сможет обнаружить изменения внутри объекта — только замену ссылки целиком.
SwiftUI реализует @State через внутренний механизм Storage. Каждое @State-свойство получает выделенную ячейку памяти, которая хранится в специальном storage-контейнере view. Когда происходит запись в 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("Hello, \(name)")
Button(isLoggedIn ? "Log Out" : "Log In") {
isLoggedIn.toggle()
}
}
}
}
В примере два @State-свойства: name (String) и isLoggedIn (Bool). При вызове isLoggedIn.toggle() SwiftUI помечает ContentView как требующую обновления и перезапускает body в следующем цикле рендера. Ключевой момент: @State-свойства всегда объявляются с модификатором private — это сигнал, что состояние принадлежит исключительно текущему view и не должно изменяться извне напрямую.
Для наблюдения за изменениями SwiftUI использует CurrentValueSubject из Combine. Каждое @State-свойство создаёт скрытый издатель, который уведомляет систему при каждом изменении. Это позволяет SwiftUI перерисовывать только минимально необходимый набор view, избегая полного обновления иерархии.
@State оптимален для простых локальных состояний: текстовые поля поиска, булевы флаги для модальных окон, переключатели настроек, счётчики, выбранные элементы списка. Если значение используется только в одном view и его вложенных компонентах (через @Binding), @State — правильный выбор. Для состояний, которые должны переживать закрытие view (например, данные формы), @State также подходит, пока view остаётся в иерархии.
Не используйте @State для глобальных состояний приложения, кэширования сетевых данных или объектов, которые используются в нескольких экранах. Для этих целей предназначены @StateObject и @EnvironmentObject. Также @State не подходит для хранения больших объёмов данных — каждый раз при изменении будет перерисовываться всё view.
@Binding — это мост между @State в родительском view и дочерним view, которому нужно изменять это состояние. Родитель объявляет @State, а дочерний компонент получает Binding через проекцию $. Изменение Binding в дочернем view автоматически обновляет @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. Оба view перерисовываются с новым значением.
Наиболее частая ошибка — присвоение класса @State-свойству. Если написать @State var model = MyClass(), SwiftUI не сможет отследить изменения свойств внутри класса — только замену самого объекта. Для классов всегда используйте @StateObject. Вторая распространённая проблема — объявление @State без модификатора private, что нарушает принцип инкапсуляции состояния.
Передача @State напрямую в дочернее view без $ — ещё одна типичная ошибка. Если передать TextField(text: text) вместо TextField(text: $text), дочерний компонент получит просто строку, а не Binding. Изменение текста в TextField не будет синхронизировано с родительским @State. Всегда используйте проекцию $ для передачи Binding.
Третья ошибка — множественные @State-свойства для связанных данных. Если несколько значений логически образуют единое целое (например, поля формы), объедините их в одну структуру с единственным @State. Это упрощает передачу состояния дочерним view и уменьшает количество отдельных триггеров обновления.
@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("Log In") {
login()
}.disabled(isLoading)
}
}
private func login() {
isLoading = true
// Perform network request
}
}
В примере четыре @State-свойства: email и password для полей формы, isLoading для индикации загрузки и errorMessage для отображения ошибок. Каждое свойство независимо управляет своей частью интерфейса. При изменении isLoading кнопка автоматически блокируется через disabled(isLoading) — без ручного обновления UI.
Часто задаваемые вопросы
@State предназначен для локального состояния конкретного view. Модификатор private гарантирует, что другие компоненты не изменят его напрямую, нарушая инкапсуляцию. Для внешнего доступа используйте проекцию $.
Да, @State поддерживает массивы и словари, поскольку это Value-типы. Однако при изменении элемента массива SwiftUI перерисовывает всё view. Для больших списков эффективнее использовать @StateObject с @Published.
@State корректно работает с Optional-типами. При присвоении nil SwiftUI обнаруживает изменение и перерисовывает view. Это удобно для состояний errorMessage: String?, где nil означает отсутствие ошибки.
@State сохраняет значение, пока view остаётся в иерархии. Если view удаляется из иерархии и добавляется снова, @State инициализируется заново со значением по умолчанию. Для персистентности используйте @AppStorage.
Да, оберните изменение в withAnimation: withAnimation(.easeInOut) { isExpanded.toggle() }. SwiftUI анимирует переход между старым и новым состоянием интерфейса с указанным типом анимации.
Итоги
Мы разработаем мобильное приложение под ключ
IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.
Читайте также