.onDisappear: اصل کار و کاربرد در SwiftUI

نویسنده: IT Sectr منتشر شده: 2026-06-26 زمان مطالعه: 10 دقیقه

.onDisappear — اصلاح‌کننده SwiftUI که هنگام حذف View از سلسله‌مراتب رابط، closure را اجرا می‌کند. فراخوانی هنگام بسته شدن صفحه، تغییر تب، dismiss پنجره مودال یا اسکرول عنصر به خارج از محدوده دید انجام می‌شود. طبق Apple Developer Documentation (2026)، onDisappear در سناریوهای خاتمه اضطراری یا نابودی برنامه توسط system-watchdog تضمین فراخوانی ندارد. درباره SwiftUI در مطلب SwiftUI بیشتر بخوانید.

نکات اصلی

  • .onDisappear — اصلاح‌کننده SwiftUI برای اجرای کد هنگام ناپدید شدن View.
  • تقارن — onDisappear جفت onAppear است و به ترتیب معکوس فراخوانی می‌شود.
  • پاکسازی منابع — سناریوی اصلی: لغو تایمرها، بستن اتصالات، ذخیره وضعیت.
  • تحلیل — از طریق onDisappear رویدادهای screen close، dwell time، session end ارسال می‌شوند.
  • عدم تضمین — onDisappear در خاتمه اضطراری برنامه فراخوانی نمی‌شود.

.onDisappear چیست؟

.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

سyntax پایه onDisappear به اندازه onAppear مختصر است. اصلاح‌کننده پارامتر اضافی نمی‌پذیرد — فقط closure که به صورت همزمان در thread اصلی اجرا می‌شود.

swift
struct DetailView: View {
    var body: some View {
        Text("کردن جزئیات")
            .onDisappear {
                print("DetailView از صفحه ناپدید شد")
            }
    }
}

.onDisappear چگونه کار می‌کند

مکانیزم فراخوانی onDisappear برعکس onAppear است: ابتدا عناصر فرزند onDisappear را دریافت می‌کنند، سپس والد. این قانون child-first تضمین می‌کند که منابع عناصر فرزند قبل از منابع والد آزاد می‌شوند. اگر عنصر فرزند به داده‌های والد وابسته است، باید بتواند بدون زمینه والد به درستی پایان یابد.

SwiftUI onDisappear را زمانی که View از گراف رندر حذف می‌شود فراخوانی می‌کند. محرک می‌تواند: pop از NavigationStack، تغییر TabView، dismiss پنجره مودال (sheet/fullScreenCover)، تغییر View با نمایش شرطی (if/switch) باشد. در List و ScrollView onDisappear هنگام اسکرول سلول به خارج از بافر prefetch فراخوانی می‌شود.

ترتیب فراخوانی child-first

ترتیب child-first به این معنی است که اگر در VStack سه View فرزند وجود داشته باشد، onDisappear ابتدا برای هر فرزند به ترتیب معکوس، سپس برای والد فراخوانی می‌شود. این برای پاکسازی صحیح حیاتی است: تایمرهای فرزند قبل از اینکه ViewModel والد منابع مشترک را آزاد کند لغو می‌شوند.

swift
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 فراخوانی می‌شود

سناریوهای فراخوانی 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

سناریوهای اصلی onDisappear — پاکسازی منابع، ذخیره وضعیت و ردیابی. برخلاف onAppear، وظایف onDisappear در خروج اجرا می‌شوند و نیاز به بررسی تکراری ندارند، زیرا View یک‌بار ناپدید می‌شود.

ذخیره وضعیت و لغو تایمرها

ذخیره وضعیت در onDisappear به ویژه برای فرم‌هایی مفید است که داده‌ها باید هنگام خروج از صفحه ذخیره شوند. تایمرها و اشتراک‌های Combine در onDisappear لغو می‌شوند تا از نشت حافظه هنگام بازگشت به صفحه جلوگیری شود.

swift
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 محاسبه می‌کنیم و رویداد تحلیلی با مدت جلسه ارسال می‌کنیم.

swift
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 vs .onAppear — تفاوت‌ها

تفاوت کلیدی onDisappear با onAppear — ترتیب فراخوانی و تضمین‌های فعال‌سازی. onAppear هنگام افزودن View به سلسله‌مراتب فراخوانی می‌شود و خاصیت فراخوانی مجدد در بازآفرینی دارد. onDisappear هنگام حذف فراخوانی می‌شود و فقط در بسته شدن عادی تضمیناً فعال می‌شود، اما در سناریوهای اضطراری نه.

طبق WWDC 2024، Apple توصیه می‌کند onDisappear را به عنوان نقطه پاکسازی در نظر بگیرید، نه نقطه ذخیره داده. داده‌های حیاتی (پرداخت‌ها، ثبت‌نام‌ها) باید در زمان واقعی ذخیره شوند، نه در لحظه ناپدید شدن View، زیرا onDisappear هنگام جمع‌شدن برنامه تضمین فراخوانی ندارد.

ویژگی.onAppear.onDisappear
زمان فراخوانیافزودن View به سلسله‌مراتبحذف View از سلسله‌مراتب
ترتیبParent-firstChild-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

WebSocket — منبعی معمول که باید هنگام خروج از صفحه بسته شود. در مثال زیر onDisappear سوکت را قطع می‌کند و onAppear دوباره آن را متصل می‌کند که صرفه‌جویی قابل توجهی در ترافیک برای برنامه‌های چندصفحه‌ای ایجاد می‌کند.

swift
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 فراخوانی نشود؟

بله، .onDisappear در خاتمه اضطراری برنامه (crash، watchdog kill)، جمع‌شدن برنامه بدون بستن صفحه یا سناریوی سیستمی که برنامه در پس‌زمینه نابود می‌شود تضمین فراخوانی ندارد. برای داده‌های حیاتی از scenePhase یا applicationWillTerminate استفاده کنید.

چگونه .onDisappear را از .scenePhase تشخیص دهیم؟

.onDisappear هنگام حذف View از سلسله‌مراتب فعال می‌شود — callback محلی برای یک صفحه خاص. .scenePhase (از طریق @Environment(\.scenePhase)) هنگام تغییر وضعیت کل برنامه فعال می‌شود: active، inactive، background. برای ردیابی زمان در صفحه از onAppear+onDisappear، برای ذخیره وضعیت جهانی از scenePhase استفاده کنید.

چرا .onDisappear چندین بار متوالی فراخوانی می‌شود؟

علت — جابجایی سریع بین تب‌ها یا push/pop مکرر یک صفحه. SwiftUI ممکن است نمونه جدیدی از View ایجاد کند، نمونه قدیمی را حذف کند، دوباره ایجاد کند — هر بار onDisappear و onAppear را فراخوانی می‌کند. بررسی کنید که از .id()، .equatable() استفاده نمی‌کنید یا View را در body والد بازآفرینی نمی‌کنید.

آیا .onDisappear در SwiftUI روی macOS کار می‌کند؟

بله، .onDisappear به طور کامل روی macOS (10.15+) با همان رفتار پشتیبانی می‌شود: هنگام بسته شدن پنجره، حذف پنل split view یا dismiss پنجره مودال فراخوانی می‌شود. روی macOS onDisappear همچنین هنگام مخفی‌شدن پنجره (hide) فراخوانی می‌شود، نه فقط هنگام بسته شدن، که برای برنامه‌های macOS مهم است.

چگونه URLSessionTask را در .onDisappear لغو کنیم؟

ارجاع به URLSessionTask را در @State ذخیره کنید و در onDisappear task.cancel() را فراخوانی کنید. به طور جایگزین از اصلاح‌کننده .task استفاده کنید که عملیات async را هنگام ناپدید شدن View به طور خودکار لغو می‌کند. .task برای تمام عملیات‌های async از جمله URLSession ترجیح داده می‌شود.

خلاصه

  • .onDisappear — اصلاح‌کننده جفت به onAppear برای پاکسازی منابع و ردیابی هنگام ناپدید شدن View.
  • ترتیب child-first — Viewهای فرزند onDisappear را زودتر از والد برای زنجیره آزادسازی صحیح دریافت می‌کنند.
  • تضمین فراخوانی وجود ندارد — در crash، جمع‌شدن یا خاتمه سیستمی onDisappear ممکن است کار نکند.
  • سناریوهای اصلی — لغو تایمرها، بستن WebSocket، ذخیره وضعیت، ردیابی مدت زمان.
  • تحلیل — onDisappear رویدادهای screen_close را با محاسبه dwell time از طریق Date().timeIntervalSince ارسال می‌کند.
  • برای عملیات‌های async — از .task به جای onDisappear با Task {} استفاده کنید، زیرا .task به طور خودکار درخواست‌ها را لغو می‌کند.

ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد

IT Sectr از سال 2017 برنامه‌های iOS و Android را برای استارتاپ‌ها و کسب‌وکارها ایجاد می‌کند. ما به شما مشاوره می‌دهیم و بهترین راه‌حل را پیشنهاد خواهیم کرد.

بحث درباره پروژه

همچنین بخوانید