.onDisappear — модификатор SwiftUI који извршава затварање при уклањању View из хијерархије интерфејса. Позив се дешава при затварању екрана, пребацивању картице, dismiss-у модалног прозора или скроловању елемента ван видљивог подручја. Према Apple Developer Documentation (2026), onDisappear не гарантује позив у сценаријима хитног завршетка или при уништењу апликације од стране system-watchdog-а. Више о SwiftUI прочитајте у материјалу о SwiftUI.
Главно
.onDisappear — модификатор View у SwiftUI који прихвата затварање Void и извршава га при уклањању View из хијерархије. У пару са .onAppear формира комплетан животни циклус екрана: појављивање — рад — нестајање. Apple је представио onDisappear заједно са изласком SwiftUI у iOS 13 као аналог viewDidDisappear из UIKit-а.
Синтаксички .onDisappear је идентичан onAppear-у: модификује било који View и прикачује затварање које позива SwiftUI компонента при уклањању приказа. За разлику од UIKit-а, где се viewDidDisappear активира тек по завршетку анимације прелаза, onDisappear у SwiftUI-ју може бити позван пре завршетка анимације — у тренутку када је View означен за уклањање.
Основна синтакса onDisappear-а је исто толико концизна као onAppear-а. Модификатор не прихвата додатне параметре — само затварање које се извршава синхроно у главној нити.
struct DetailView: View {
var body: some View {
Text("Детаљни екран")
.onDisappear {
print("DetailView је нестао са екрана")
}
}
}
Механизам позива onDisappear-а је супротан onAppear-у: прво подређени елементи добијају onDisappear, затим родитељ. Ово правило child-first гарантује да се ресурси подређених елемената ослобађају пре ослобађања ресурса родитеља. Ако подређени елемент зависи од података родитеља, мора имати могућност да се правилно заврши без родитељског контекста.
SwiftUI позива onDisappear у тренутку када се View искључује из графа рендеровања. Окидач може бити: pop из NavigationStack-а, пребацивање TabView-а, dismiss модалног прозора (sheet/fullScreenCover), промена условно приказаног View-а (if/switch). У List и ScrollView onDisappear се позива при скроловању ћелије ван prefetch бафера.
Редослед child-first значи да ако су у VStack-у три подређена View-а, onDisappear ће бити позван прво за свако дете обрнутим редом, затим за родитеља. Ово је критично за правилно чишћење: подређени тајмери се отказују пре него што родитељски ViewModel ослободи дељене ресурсе.
struct ParentView: View {
var body: some View {
VStack {
ChildView(id: "A")
ChildView(id: "B")
}
.onDisappear {
print("Parent onDisappear — последњи")
}
}
}
struct ChildView: View {
let id: String
var body: some View {
Text("Дете \(id)")
.onDisappear {
print("Дете \(id) onDisappear")
}
}
}
Излаз у конзолу: Child B onDisappear, Child A onDisappear, Parent onDisappear — последњи. Обрнут редослед у поређењу са onAppear-ом гарантује исправан ланац ослобађања.
Сценарији позива onDisappear-а зависе од типа контејнера. У NavigationStack-у onDisappear се активира при pop-to-root, обичном pop-back-у или уклањању екрана гестом превлачења (interactivePopGestureRecognizer). У TabView-у пребацивање картице позива onDisappear за скривену и onAppear за приказану — оба модификатора се активирају готово истовремено.
У Sheet и fullScreenCover onDisappear се позива при програмском dismiss-у (путем @Environment(\.dismiss)) или гестом превлачења надоле. Важна карактеристика: ако је sheet отворен, али је корисник пребацио апликацију, onDisappear се НЕ позива до стварног затварања.
| Сценариј | onDisappear се позива | Напомена |
|---|---|---|
| Pop у NavigationStack | Да | Одмах после анимације |
| Пребацивање картице TabView | Да | Тренутна картица |
| Dismiss sheet | Да | Пре завршетка анимације |
| Скрол у List | Да | Ћелија изашла из prefetch зоне |
| Скривање апликације | Не | Нема гаранције позива |
| Crash/watchdog kill | Не | Не позива се |
Основни сценарији onDisappear-а — чишћење ресурса, чување стања и праћење. За разлику од onAppear-а, задаци onDisappear-а се извршавају на излазу и не захтевају проверу дуплирања, јер View нестаје једнократно.
Чување стања у onDisappear-у је посебно корисно за форме где подаци морају бити сачувани при напуштању екрана. Тајмери и Combine претплате се отказују у onDisappear-у како би се избегло цурење меморије при повратку на екран.
struct FormView: View {
@State private var draftText = ""
@State private var timer: Timer?
var body: some View {
TextField("Enter text", text: $draftText)
.onAppear {
timer = Timer.scheduledTimer(withTimeInterval: 60, repeats: true) { _ in
saveDraft()
}
}
.onDisappear {
timer?.invalidate()
timer = nil
saveDraft()
}
}
private func saveDraft() {
UserDefaults.standard.set(draftText, forKey: "draft")
}
}
Отказивање тајмера у onDisappear-у спречава извршавање кода након што је екран већ затворен. Без отказивања, тајмер може покушати да ажурира @State који више не припада актуелном View-у, изазивајући runtime упозорење.
Дужина боравка на екрану — класичан сценариј праћења. Бележимо време у onAppear-у, израчунавамо разлику у onDisappear-у и шаљемо аналитички догађај са трајањем сесије.
struct TrackedView: View {
@State private var appearTime: Date?
var body: some View {
Text("Праћени екран")
.onAppear {
appearTime = Date()
Analytics.shared.logEvent("screen_view", params: ["screen": "TrackedView"])
}
.onDisappear {
if let start = appearTime {
let duration = Date().timeIntervalSince(start)
Analytics.shared.logEvent("screen_close", params: [
"screen": "TrackedView",
"duration_ms": Int(duration * 1000)
])
}
}
}
}
Кључна разлика onDisappear-а од onAppear-а — редослед позива и гаранције активирања. onAppear се позива при додавању View-а у хијерархију и има својство поновног позивања при рекреирању. onDisappear се позива при уклањању и гарантовано ради само при нормалном затварању, али не у хитним сценаријима.
Према WWDC 2024, Apple препоручује да се onDisappear посматра као тачка чишћења, а не као тачка чувања података. Критично важни подаци (плаћања, регистрације) треба да се чувају у реалном времену, а не у тренутку нестанка View-а, јер onDisappear не гарантује позив при скривању апликације.
| Карактеристика | .onAppear | .onDisappear |
|---|---|---|
| Тренутак позива | Додавање View у хијерархију | Уклањање View из хијерархије |
| Редослед | Parent-first | Child-first |
| Гаранција | Висока | Нема при хитним завршецима |
| Основни задатак | Иницијализација | Чишћење |
| Поновни позив | При рекреирању View | Једнократно по нестанку |
Препорука: користите onDisappear само за неcritično чишћење и праћење. За чување података користите scenePhase или applicationWillTerminate обавештења у AppDelegate-у.
Сценариј 1: YouTube-слично учитавање. На екрану детаља видеа onDisappear чува позицију репродукције у UserDefaults-у. При поновном отварању onAppear враћа позицију из UserDefaults-а, дајући ефекат непрекидног гледања.
Сценариј 2: отказивање Combine претплате. Ако ViewModel користи Combine publisher-е, onDisappear отказује претплату путем cancellable?.cancel(). Ово спречава ажурирање UI-ја након напуштања екрана, што је посебно важно за листе са пагинацијом и упите претраге.
Сценариј 3: затварање WebSocket-а. У апликацијама са real-time везама (месенџери, токови котација) onDisappear затвара WebSocket везу ради уштеде батерије и саобраћаја. Поновно отварање се врши у onAppear-у при повратку на екран.
WebSocket — типичан ресурс који треба затворити при напуштању екрана. У примеру испод onDisappear искључује socket, а onAppear га поново повезује, што даје значајну уштеду саобраћаја за апликације са више екрана.
struct ChatView: View {
@StateObject private var socket = WebSocketManager()
var body: some View {
ChatListView(messages: socket.messages)
.onAppear {
socket.connect()
}
.onDisappear {
socket.disconnect()
}
}
}
Важно: при пребацивању између картица TabView-а, onDisappear тренутне и onAppear следеће картице се активирају готово истовремено. За WebSocket ово може довести до циклуса disconnect-connect који ствара додатно оптерећење. Решење — коришћење кашњења или провера да ли је socket потребан на следећем екрану.
Често постављана питања
Да, .onDisappear не гарантује позив при хитном завршетку апликације (crash, watchdog kill), скривању апликације без затварања екрана или системском сценарију где се апликација уништава у позадини. За критично важне податке користите scenePhase или applicationWillTerminate.
.onDisappear се активира при уклањању View-а из хијерархије — локални повратни позив за конкретан екран. .scenePhase (путем @Environment(\.scenePhase)) се активира при промени стања целе апликације: active, inactive, background. За праћење времена на екрану користите onAppear+onDisappear, за чување глобалног стања — scenePhase.
Разлог — брзо пребацивање између картица или вишеструки push/pop истог екрана. SwiftUI може креирати нову инстанцу View-а, уклонити стару, поново креирати — сваки пут позивајући onDisappear и onAppear. Проверите да ли користите .id(), .equatable() или да ли поново креирате View у родитељском телу.
Да, .onDisappear је потпуно подржан на macOS-у (10.15+) са истим понашањем: позива се при затварању прозора, уклањању split view панела или dismiss-у модалног прозора. На macOS-у onDisappear се такође позива при скривању прозора (hide), а не само при затварању, што је важно за macOS апликације.
Сачувајте референцу на URLSessionTask у @State и позовите task.cancel() у onDisappear-у. Алтернативно користите .task модификатор који аутоматски отказује async операцију при нестанку View-а. .task је пожељнији за све async операције, укључујући URLSession.
Итог
Развићемо мобилну апликацију под кључ
IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође