@State — что это, назначение и использование в SwiftUI

Автор: IT Sectr Опубликовано: 2026-06-19 Время чтения: 7 мин

@State — это Property Wrapper в SwiftUI для управления локальным состоянием внутри одного представления. SwiftUI автоматически перерисовывает view при каждом изменении @State-свойства, делая интерфейс реактивным без ручных вызовов обновления. По данным Apple Developer Documentation (2025), @State рекомендуется для простых типов и структур, принадлежащих одному view. @State — самый простой способ добавить интерактивность в SwiftUI-интерфейс.

Главное

  • @State — Property Wrapper для локального состояния, принадлежащего одному view
  • Автоматическое обновление — SwiftUI перезапускает body при изменении @State-свойства
  • Простые типы — @State подходит для String, Int, Bool, enum и структур
  • Не передавать вложенным view — для изменения из дочерних компонентов используйте @Binding
  • private — @State-свойства всегда объявляются с модификатором private

Что такое @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 не сможет обнаружить изменения внутри объекта — только замену ссылки целиком.

Как работает @State под капотом?

SwiftUI реализует @State через внутренний механизм Storage. Каждое @State-свойство получает выделенную ячейку памяти, которая хранится в специальном storage-контейнере view. Когда происходит запись в wrappedValue, SwiftUI через didSet уведомляет свой график зависимостей (dependency graph) о необходимости перерисовки.

swift
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 в проекте

@State оптимален для простых локальных состояний: текстовые поля поиска, булевы флаги для модальных окон, переключатели настроек, счётчики, выбранные элементы списка. Если значение используется только в одном view и его вложенных компонентах (через @Binding), @State — правильный выбор. Для состояний, которые должны переживать закрытие view (например, данные формы), @State также подходит, пока view остаётся в иерархии.

  • Текстовые поля — @State для хранения введённого текста в TextField
  • Булевы флаги — @State для показа/скрытия модальных окон и sheet
  • Выбор элементов — @State для отслеживания выбранной вкладки или строки
  • Счётчики — @State для числовых значений с инкрементом/декрементом
  • Промежуточные вычисления — @State для кэширования результатов внутри view

Не используйте @State для глобальных состояний приложения, кэширования сетевых данных или объектов, которые используются в нескольких экранах. Для этих целей предназначены @StateObject и @EnvironmentObject. Также @State не подходит для хранения больших объёмов данных — каждый раз при изменении будет перерисовываться всё view.

@State и @Binding: совместная работа

@Binding — это мост между @State в родительском view и дочерним view, которому нужно изменять это состояние. Родитель объявляет @State, а дочерний компонент получает Binding через проекцию $. Изменение Binding в дочернем view автоматически обновляет @State в родителе — и наоборот. Это обеспечивает однонаправленный поток данных с возможностью обратной связи.

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)
    }
}

В листинге ParentView владеет @State text, а ChildView получает $text как Binding. TextField внутри ChildView привязывается к этому Binding через text: $text. Когда пользователь печатает в TextField, значение изменяется в ChildView через Binding, что вызывает обновление @State в ParentView. Оба view перерисовываются с новым значением.

Типичные ошибки при работе с @State

Наиболее частая ошибка — присвоение класса @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 применяется в большинстве SwiftUI-проектов для базовой интерактивности. Рассмотрим пример формы входа, где @State управляет текстовыми полями и состоянием загрузки. Такой паттерн встречается в каждом приложении — от простых заметок до сложных корпоративных решений.

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("Log In") {
                login()
            }.disabled(isLoading)
        }
    }

    private func login() {
        isLoading = true
        // Perform network request
    }
}

В примере четыре @State-свойства: email и password для полей формы, isLoading для индикации загрузки и errorMessage для отображения ошибок. Каждое свойство независимо управляет своей частью интерфейса. При изменении isLoading кнопка автоматически блокируется через disabled(isLoading) — без ручного обновления UI.

Часто задаваемые вопросы

Почему @State объявляется с private?

@State предназначен для локального состояния конкретного view. Модификатор private гарантирует, что другие компоненты не изменят его напрямую, нарушая инкапсуляцию. Для внешнего доступа используйте проекцию $.

Может ли @State содержать массив или словарь?

Да, @State поддерживает массивы и словари, поскольку это Value-типы. Однако при изменении элемента массива SwiftUI перерисовывает всё view. Для больших списков эффективнее использовать @StateObject с @Published.

Что произойдёт при присвоении nil @State-свойству с Optional-типом?

@State корректно работает с Optional-типами. При присвоении nil SwiftUI обнаруживает изменение и перерисовывает view. Это удобно для состояний errorMessage: String?, где nil означает отсутствие ошибки.

Как @State ведёт себя при повторном появлении view?

@State сохраняет значение, пока view остаётся в иерархии. Если view удаляется из иерархии и добавляется снова, @State инициализируется заново со значением по умолчанию. Для персистентности используйте @AppStorage.

Можно ли анимировать изменения @State?

Да, оберните изменение в withAnimation: withAnimation(.easeInOut) { isExpanded.toggle() }. SwiftUI анимирует переход между старым и новым состоянием интерфейса с указанным типом анимации.

Итоги

  • @State — Property Wrapper для локального состояния одного view, автоматически обновляющий интерфейс
  • Подходит для простых типов: String, Int, Bool, а также структур и enum
  • Не подходит для ссылочных типов (классов) — используйте @StateObject
  • Всегда private — состояние не должно изменяться извне напрямую
  • Проекция $ — создаёт Binding для передачи права изменения дочерним view
  • Несколько @State в одном view — нормальная практика для независимых состояний
  • withAnimation — позволяет анимировать изменения @State-свойств

Мы разработаем мобильное приложение под ключ

IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.

Обсудить проект

Читайте также