@State и локальное состояние вью
@State — это способ дать вью собственную изменяемую память. Когда состояние меняется, SwiftUI автоматически перерисовывает вью.
Суть урока: вью — структура, а значит неизменяема. @State — это обёртка (property wrapper), которая хранит данные ВНЕ структуры и связывает их со SwiftUI, делая вью реактивным.
Возникает парадокс: вью — это структура, её свойства неизменяемы, но интерфейс должен меняться. Решение — property wrapper @State. Он хранит значение в специальном месте, которым управляет SwiftUI, а не внутри самой структуры:
struct ToggleView: View {
@State private var isOn = false
var body: some View {
VStack {
Text(isOn ? "Включено" : "Выключено")
Button("Переключить") {
isOn.toggle() // меняем — вью перерисуется
}
}
}
}Хотя body формально не может менять свойства структуры, @State позволяет это: при изменении isOn SwiftUI замечает, что вью зависело от этого значения, и заново вызывает body. @State-свойство — это источник истины (source of truth) для данных, которыми владеет именно это вью.
Важное правило: @State почти всегда помечают private. Оно принадлежит только этому вью; другие вью не должны лезть в него напрямую. И используется оно для простых, локальных данных — флажков, введённого текста, выбранной вкладки.
Структура вью (неизменяема)
+-------------------------------+
| @State private var isOn |---+
+-------------------------------+ |
v
SwiftUI хранит значение ОТДЕЛЬНО
| isOn меняется
v
body вызывается заново -> экран обновлёнПопробуй сам ▶ — запусти код прямо в браузере (Pyodide). Здесь нет Swift, но логика та же, что под капотом мобильного кода:
# Источник истины как единственное место хранения состояния.
class ViewState:
def __init__(self):
self._is_on = False # хранится отдельно, как @State
def toggle(self):
self._is_on = not self._is_on
self.render() # SwiftUI вызвал бы body сам
def render(self):
print('Включено' if self._is_on else 'Выключено')
v = ViewState()
v.render()
v.toggle()
v.toggle()Как работает под капотом
Property wrapper — это обёртка над свойством с дополнительной логикой. @State прячет значение в управляемом SwiftUI хранилище, привязанном к идентичности вью, а не к экземпляру структуры (которая пересоздаётся при каждой перерисовке). Когда вы меняете значение, SwiftUI помечает вью как «грязное» и планирует пересчёт его body. Так данные переживают пересоздание структуры, а интерфейс остаётся синхронным с состоянием.
Частые ошибки
- Использовать @State для данных, которыми владеет другое вью. Тогда нужен @Binding или @Observable.
- Делать @State не приватным. Это нарушает инкапсуляцию владения.
- Хранить в @State тяжёлые модели. Он задуман для простого локального состояния.
Best practices
- Помечайте @State как private — состояние принадлежит только этому вью.
- Используйте @State для лёгких локальных данных: флажков, текста, выбора.
- Для общих и сложных данных переходите к @Observable из следующих уроков.
Итоги. @State даёт вью собственную реактивную память, хранимую SwiftUI вне неизменяемой структуры. Это источник истины для локальных данных. Дальше научимся делиться состоянием между вью через @Binding.
Шире контекста
Property wrappers вроде @State — одна из самых изящных идей Swift: они позволяют прикрепить к свойству дополнительное поведение, спрятав сложную механику за простым синтаксисом. Снаружи @State private var isOn = false выглядит как обычная переменная, но внутри это целая система хранения и оповещения, связанная с движком отрисовки SwiftUI. Ключевая тонкость для понимания: структура вью пересоздаётся при каждой перерисовке, а вот хранилище @State живёт дольше, привязанное к идентичности вью в дереве, а не к конкретному экземпляру структуры. Именно поэтому ваш счётчик не сбрасывается при каждом обновлении экрана. Правильное разделение ответственности — @State для простого локального состояния, @Observable для разделяемых моделей — это то, что отличает чистую SwiftUI-архитектуру от запутанной. На старте достаточно запомнить: маленькие локальные данные, которыми владеет именно это вью, — это работа для @State.