.onDisappear — اصلاحکننده SwiftUI که هنگام حذف View از سلسلهمراتب رابط، closure را اجرا میکند. فراخوانی هنگام بسته شدن صفحه، تغییر تب، dismiss پنجره مودال یا اسکرول عنصر به خارج از محدوده دید انجام میشود. طبق Apple Developer Documentation (2026)، onDisappear در سناریوهای خاتمه اضطراری یا نابودی برنامه توسط system-watchdog تضمین فراخوانی ندارد. درباره SwiftUI در مطلب SwiftUI بیشتر بخوانید.
نکات اصلی
.onDisappear — اصلاحکننده View در SwiftUI که یک closure از نوع Void میپذیرد و هنگام حذف View از سلسلهمراتب آن را اجرا میکند. در جفت با .onAppear چرخه کامل حیات صفحه را تشکیل میدهد: ظهور — کار — ناپدید شدن. Apple onDisappear را همراه با انتشار SwiftUI در iOS 13 به عنوان مشابه viewDidDisappear از UIKit معرفی کرد.
از نظر نحوی .onDisappear مشابه onAppear است: هر View را اصلاح میکند و closureای را متصل میکند که توسط کامپونیت SwiftUI هنگام حذف نمایش فراخوانی میشود. برخلاف UIKit که viewDidDisappear فقط پس از اتمام انیمیشن انتقال فعال میشود، onDisappear در SwiftUI ممکن است قبل از اتمام انیمیشن — در لحظهای که View برای حذف علامتگذاری میشود — فراخوانی شود.
سyntax پایه onDisappear به اندازه onAppear مختصر است. اصلاحکننده پارامتر اضافی نمیپذیرد — فقط closure که به صورت همزمان در thread اصلی اجرا میشود.
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 معمولی یا حذف صفحه با حرکت swipe (interactivePopGestureRecognizer) فعال میشود. در TabView تغییر تب باعث onDisappear برای تب مخفیشونده و onAppear برای تب نمایشدادهشده میشود — هر دو اصلاحکننده تقریباً همزمان فعال میشوند.
در Sheet و fullScreenCover onDisappear هنگام dismiss برنامهریزیشده (از طریق @Environment(\.dismiss)) یا حرکت swipe به پایین فراخوانی میشود. ویژگی مهم: اگر 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 warning میشود.
مدت زمان حضور در صفحه — سناریوی کلاسیک ردیابی. زمان را در 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 فقط برای پاکسازی غیرحیاتی و ردیابی استفاده کنید. برای ذخیره دادهها از scenePhase یا اعلانهای applicationWillTerminate در AppDelegate استفاده کنید.
سناریوی 1: بارگذاری مشابه YouTube. در صفحه جزئیات ویدیو onDisappear موقعیت پخش را در UserDefaults ذخیره میکند. در بازگشایی مجدد، onAppear موقعیت را از UserDefaults بازیابی میکند و اثر تماشای پیوسته را ایجاد میکند.
سناریوی 2: لغو اشتراک Combine. اگر ViewModel از publisherهای Combine استفاده میکند، onDisappear اشتراک را از طریق cancellable?.cancel() لغو میکند. این کار از بهروزرسانی UI پس از خروج از صفحه جلوگیری میکند که برای لیستهای صفحهبندیشده و جستجوها مهم است.
سناریوی 3: بستن WebSocket. در برنامههای با اتصالات بلادرنگ (پیامرسانها، فیدهای قیمت) onDisappear اتصال WebSocket را برای صرفهجویی در باتری و ترافیک میبندد. بازگشایی مجدد در onAppear هنگام بازگشت به صفحه انجام میشود.
WebSocket — منبعی معمول که باید هنگام خروج از صفحه بسته شود. در مثال زیر onDisappear سوکت را قطع میکند و 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 منجر شود که بار اضافی ایجاد میکند. راهحل — استفاده از تأخیر یا بررسی نیاز به سوکت در صفحه بعدی.
سوالات متداول
بله، .onDisappear در خاتمه اضطراری برنامه (crash، watchdog kill)، جمعشدن برنامه بدون بستن صفحه یا سناریوی سیستمی که برنامه در پسزمینه نابود میشود تضمین فراخوانی ندارد. برای دادههای حیاتی از scenePhase یا applicationWillTerminate استفاده کنید.
.onDisappear هنگام حذف View از سلسلهمراتب فعال میشود — callback محلی برای یک صفحه خاص. .scenePhase (از طریق @Environment(\.scenePhase)) هنگام تغییر وضعیت کل برنامه فعال میشود: active، inactive، background. برای ردیابی زمان در صفحه از onAppear+onDisappear، برای ذخیره وضعیت جهانی از scenePhase استفاده کنید.
علت — جابجایی سریع بین تبها یا push/pop مکرر یک صفحه. SwiftUI ممکن است نمونه جدیدی از View ایجاد کند، نمونه قدیمی را حذف کند، دوباره ایجاد کند — هر بار onDisappear و onAppear را فراخوانی میکند. بررسی کنید که از .id()، .equatable() استفاده نمیکنید یا View را در body والد بازآفرینی نمیکنید.
بله، .onDisappear به طور کامل روی macOS (10.15+) با همان رفتار پشتیبانی میشود: هنگام بسته شدن پنجره، حذف پنل split view یا dismiss پنجره مودال فراخوانی میشود. روی macOS onDisappear همچنین هنگام مخفیشدن پنجره (hide) فراخوانی میشود، نه فقط هنگام بسته شدن، که برای برنامههای macOS مهم است.
ارجاع به URLSessionTask را در @State ذخیره کنید و در onDisappear task.cancel() را فراخوانی کنید. به طور جایگزین از اصلاحکننده .task استفاده کنید که عملیات async را هنگام ناپدید شدن View به طور خودکار لغو میکند. .task برای تمام عملیاتهای async از جمله URLSession ترجیح داده میشود.
خلاصه
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.
همچنین بخوانید