На первый взгляд, onAppear кажется простым и понятным: показалась вью - сработало, скрылась - сработало onDisappear. Но на практике все сложнее. В зависимости от того, где находится вью - в TabView, NavigationStack или List - поведение может кардинально отличаться. И если не понимать этих нюансов, можно нарваться на баги, которые трудно воспроизвести.
Две разные системы:
Чтобы понять, что происходит, нужно разделить два понятия: время жизни узла (node lifetime) и видимость на экране (visibility). Узел живет, пока существует идентичность вью. К узлу привязаны
@State и @StateObject. А видимость - это то, видит ли пользователь вью сейчас. onAppear и onDisappear реагируют именно на видимость, а не на создание или уничтожение узла.В простых случаях эти две шкалы совпадают. Но в сложных контейнерах - расходятся.
TabView - узлы живут, видимость меняется:
Когда вы переключаете вкладки, узлы вью не уничтожаются. Они остаются в памяти, меняется только флаг видимости. Поэтому onAppear срабатывает при каждом переключении на вкладку, но
@State сохраняет свое значение.Нюанс: на iOS 17 TabView создавал все вкладки сразу при запуске. На iOS 18+ - только при первом открытии. Один и тот же код может вести себя по-разному на разных версиях.
NavigationStack - при возврате узел уничтожается:
В навигационном стеке при возврате назад (pop) узел детальной вью уничтожается, а
@State сбрасывается. При повторном переходе создается новый узел. В отличие от TabView, здесь данные не сохраняются между показами.List и LazyVStack - узлы создаются и уничтожаются при скролле:
В ленивых списках узлы для ячеек создаются, когда те подъезжают к видимой области, и могут быть уничтожены, когда уезжают далеко. Поэтому
@State внутри ячейки нельзя использовать для хранения данных - при повторном появлении оно сбросится.Главное правило - body выполняется до onAppear:
body вычисляется до того, как сработает onAppear или .task. Нельзя полагаться на то, что в onAppear данные подгрузятся до первого вызова body. Нужно всегда предусматривать начальное состояние.
🔗 Читать подробнее
💡 Вывод:
onAppear в SwiftUI - это не «вью создана», а «вью стала видимой». Время жизни узла и видимость - это две независимые шкалы. Понимание этого спасает от багов, когда
@State вдруг не сбрасывается или, наоборот, сбрасывается не вовремя. Разные контейнеры ведут себя по-разному и это нужно тестировать, а не гадать.Подписаться на канал:
➡️ Telegram | Max
Закрытый канал:
🚀 Мобильный трудоголик PRO
