.onDisappear: nguyên lý hoạt động và ứng dụng trong SwiftUI

Tác giả: IT Sectr Đã đăng: 2026-06-26 Thời gian đọc: 10 phút

.onDisappear là bộ điều chỉnh SwiftUI thực thi một closure khi View bị xóa khỏi hệ thống phân cấp giao diện. Lệnh gọi xảy ra khi đóng màn hình, chuyển tab, loại bỏ cửa sổ phương thức hoặc cuộn phần tử ra khỏi tầm nhìn. Theo Tài liệu dành cho nhà phát triển Apple (2026), onDisappear không đảm bảo gọi trong các kịch bản sự cố hoặc khi ứng dụng bị kết thúc bởi watchdog hệ thống. Tìm hiểu thêm về SwiftUI trong bài viết về SwiftUI.

Những điểm chính

  • .onDisappear — bộ điều chỉnh SwiftUI để thực thi mã khi View biến mất.
  • Tính đối xứng — onDisappear là cặp của onAppear và được gọi theo thứ tự ngược lại.
  • Dọn dẹp tài nguyên — kịch bản chính: hủy bộ đếm thời gian, đóng kết nối, lưu trạng thái.
  • Phân tích — thông qua onDisappear gửi các sự kiện đóng màn hình, thời gian lưu lại, kết thúc phiên.
  • Không đảm bảo — onDisappear không được gọi khi ứng dụng gặp sự cố.

.onDisappear là gì?

.onDisappear là bộ điều chỉnh View trong SwiftUI nhận một closure Void và thực thi nó khi View bị xóa khỏi hệ thống phân cấp. Cùng với .onAppear, nó tạo thành vòng đời hoàn chỉnh của màn hình: xuất hiện — làm việc — biến mất. Apple giới thiệu onDisappear cùng với SwiftUI trong iOS 13 như một phiên bản tương tự của viewDidDisappear từ UIKit.

Về mặt cú pháp, .onDisappear giống hệt onAppear: nó sửa đổi bất kỳ View nào và đính kèm một closure mà trình kết xuất SwiftUI gọi khi view bị xóa. Không giống như UIKit, nơi viewDidDisappear chỉ kích hoạt sau khi hoàn thành hoạt ảnh chuyển tiếp, onDisappear trong SwiftUI có thể được gọi trước khi hoạt ảnh kết thúc — tại thời điểm View được đánh dấu để xóa.

Cú pháp onDisappear

Cú pháp cơ bản của onDisappear cũng ngắn gọn như onAppear. Bộ điều chỉnh không nhận tham số bổ sung — chỉ có closure, thực thi đồng bộ trên luồng chính.

swift
struct DetailView: View {
    var body: some View {
        Text("Detail Screen")
            .onDisappear {
                print("DetailView đã biến mất khỏi màn hình")
            }
    }
}

Cách .onDisappear hoạt động

Cơ chế gọi của onDisappear ngược với onAppear: các phần tử con nhận onDisappear trước, sau đó đến cha. Quy tắc child-first này đảm bảo tài nguyên con được giải phóng trước khi tài nguyên cha được giải phóng. Nếu phần tử con phụ thuộc vào dữ liệu cha, nó phải có khả năng kết thúc chính xác mà không cần ngữ cảnh cha.

SwiftUI gọi onDisappear khi View bị xóa khỏi đồ thị kết xuất. Các trình kích hoạt bao gồm: pop từ NavigationStack, chuyển đổi TabView, loại bỏ phương thức (sheet/fullScreenCover) hoặc View được kết xuất có điều kiện (if/switch). Trong List và ScrollView, onDisappear được gọi khi một ô cuộn ra ngoài bộ đệm tải trước.

Thứ tự gọi child-first

Thứ tự child-first có nghĩa là nếu VStack có ba View con, onDisappear được gọi trước cho mỗi con theo thứ tự ngược lại, sau đó cho cha. Điều này rất quan trọng để dọn dẹp đúng cách: bộ đếm thời gian con bị hủy trước khi ViewModel cha giải phóng tài nguyên dùng chung.

swift
struct ParentView: View {
    var body: some View {
        VStack {
            ChildView(id: "A")
            ChildView(id: "B")
        }
        .onDisappear {
            print("Parent onDisappear — cuối cùng")
        }
    }
}

struct ChildView: View {
    let id: String
    var body: some View {
        Text("Child \(id)")
            .onDisappear {
                print("Child \(id) onDisappear")
            }
    }
}

Đầu ra bảng điều khiển: Child B onDisappear, Child A onDisappear, Parent onDisappear — cuối cùng. Thứ tự ngược lại so với onAppear đảm bảo chuỗi dọn dẹp chính xác.

Khi nào .onDisappear được gọi

Các kịch bản gọi của onDisappear phụ thuộc vào loại vùng chứa. Trong NavigationStack, onDisappear kích hoạt khi pop-to-root, pop-back thông thường hoặc loại bỏ màn hình bằng cách vuốt (interactivePopGestureRecognizer). Trong TabView, chuyển tab gọi onDisappear cho tab ẩn và onAppear cho tab hiển thị — cả hai bộ điều chỉnh kích hoạt gần như đồng thời.

Trong Sheet và fullScreenCover, onDisappear được gọi khi loại bỏ theo chương trình (qua @Environment(\.dismiss)) hoặc cử chỉ vuốt xuống. Một chi tiết quan trọng: nếu sheet đã được mở nhưng người dùng đã chuyển ứng dụng, onDisappear sẽ KHÔNG được gọi cho đến khi đóng thực tế.

Kịch bảnonDisappear được gọiGhi chú
Pop trong NavigationStackNgay sau hoạt ảnh
Chuyển tab TabViewTab hiện tại
Loại bỏ sheetTrước khi hoàn thành hoạt ảnh
Cuộn trong ListÔ đã ra khỏi vùng tải trước
Thu nhỏ ứng dụngKhôngKhông đảm bảo gọi
Sự cố/watchdog killKhôngKhông được gọi

Ví dụ về .onDisappear

Các trường hợp sử dụng chính của onDisappear là dọn dẹp tài nguyên, lưu trạng thái và theo dõi. Không giống như onAppear, các tác vụ onDisappear thực thi khi thoát và không cần kiểm tra trùng lặp vì View biến mất một lần.

Lưu trạng thái và hủy bộ đếm thời gian

Lưu trạng thái trong onDisappear đặc biệt hữu ích cho các biểu mẫu nơi dữ liệu phải được lưu khi rời khỏi màn hình. Bộ đếm thời gian và đăng ký Combine bị hủy trong onDisappear để tránh rò rỉ khi quay lại màn hình.

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")
    }
}

Hủy bộ đếm thời gian trong onDisappear ngăn thực thi mã sau khi màn hình đã đóng. Nếu không hủy, bộ đếm thời gian có thể cố gắng cập nhật @State không còn thuộc về View hiện tại, gây ra cảnh báo thời gian chạy.

Theo dõi thời gian trên màn hình

Thời gian lưu lại trên màn hình là một kịch bản theo dõi cổ điển. Ghi lại thời gian trong onAppear, tính toán chênh lệch trong onDisappear và gửi sự kiện phân tích với thời lượng phiên.

swift
struct TrackedView: View {
    @State private var appearTime: Date?
    
    var body: some View {
        Text("Tracked Screen")
            .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 — sự khác biệt

Sự khác biệt chính giữa onDisappear và onAppear là thứ tự gọi và đảm bảo thực thi. onAppear được gọi khi View được thêm vào hệ thống phân cấp và có thể được gọi lại khi tạo lại. onDisappear được gọi khi xóa và chỉ được đảm bảo kích hoạt khi đóng bình thường, không phải trong các kịch bản sự cố.

Theo WWDC 2024, Apple khuyến nghị coi onDisappear là điểm dọn dẹp, không phải điểm cam kết dữ liệu. Dữ liệu quan trọng (thanh toán, đăng ký) nên được lưu theo thời gian thực, không phải tại thời điểm View biến mất, vì onDisappear không được đảm bảo khi ứng dụng bị thu nhỏ.

Đặc điểm.onAppear.onDisappear
Thời điểm gọiView được thêm vào hệ thống phân cấpView bị xóa khỏi hệ thống phân cấp
Thứ tựParent-firstChild-first
Đảm bảoCaoKhông khi sự cố
Nhiệm vụ chínhKhởi tạoDọn dẹp
Gọi lạiKhi tạo lại ViewMột lần mỗi lần biến mất

Khuyến nghị: chỉ sử dụng onDisappear cho việc dọn dẹp không quan trọng và theo dõi. Để duy trì dữ liệu, hãy sử dụng scenePhase hoặc thông báo applicationWillTerminate trong AppDelegate.

Các kịch bản sử dụng điển hình

Kịch bản 1: phát lại kiểu YouTube. Trên màn hình chi tiết video, onDisappear lưu vị trí phát lại vào UserDefaults. Khi mở lại, onAppear khôi phục vị trí từ UserDefaults, tạo trải nghiệm xem liên tục.

Kịch bản 2: hủy đăng ký Combine. Nếu ViewModel sử dụng nhà xuất bản Combine, onDisappear hủy đăng ký qua cancellable?.cancel(). Điều này ngăn cập nhật UI sau khi rời khỏi màn hình, đặc biệt quan trọng cho danh sách có phân trang và truy vấn tìm kiếm.

Kịch bản 3: đóng WebSocket. Trong các ứng dụng có kết nối thời gian thực (trình nhắn tin, nguồn cấp dữ liệu báo giá), onDisappear đóng kết nối WebSocket để tiết kiệm pin và dữ liệu. Kết nối lại xảy ra trong onAppear khi quay lại màn hình.

Đóng WebSocket trong onDisappear

WebSocket là tài nguyên điển hình cần được đóng khi rời khỏi màn hình. Trong ví dụ dưới đây, onDisappear ngắt kết nối socket và onAppear kết nối lại, mang lại tiết kiệm dữ liệu đáng kể cho các ứng dụng có nhiều màn hình.

swift
struct ChatView: View {
    @StateObject private var socket = WebSocketManager()
    
    var body: some View {
        ChatListView(messages: socket.messages)
            .onAppear {
                socket.connect()
            }
            .onDisappear {
                socket.disconnect()
            }
    }
}

Quan trọng: khi chuyển đổi giữa các tab TabView, onDisappear của tab hiện tại và onAppear của tab tiếp theo kích hoạt gần như đồng thời. Đối với WebSocket, điều này có thể gây ra chu kỳ ngắt kết nối-kết nối lại tạo ra tải không cần thiết. Giải pháp là sử dụng độ trễ hoặc kiểm tra xem socket có cần thiết trên màn hình tiếp theo không.

Các câu hỏi thường gặp

.onDisappear có thể không được gọi không?

Có, .onDisappear không đảm bảo gọi khi ứng dụng gặp sự cố (crash, watchdog kill), thu nhỏ ứng dụng mà không đóng màn hình hoặc các kịch bản hệ thống nơi ứng dụng bị kết thúc trong nền. Đối với dữ liệu quan trọng, hãy sử dụng scenePhase hoặc applicationWillTerminate.

Phân biệt .onDisappear với .scenePhase như thế nào?

.onDisappear kích hoạt khi View bị xóa khỏi hệ thống phân cấp — một callback cục bộ cho một màn hình cụ thể. .scenePhase (qua @Environment(\.scenePhase)) kích hoạt khi trạng thái toàn bộ ứng dụng thay đổi: active, inactive, background. Sử dụng onAppear+onDisappear để theo dõi thời gian trên màn hình và scenePhase để duy trì trạng thái toàn cầu.

Tại sao .onDisappear được gọi nhiều lần liên tiếp?

Nguyên nhân là chuyển đổi tab nhanh hoặc push/pop lặp lại cùng một màn hình. SwiftUI có thể tạo một phiên bản View mới, xóa cái cũ, tạo lại — mỗi lần gọi onDisappear và onAppear. Kiểm tra xem bạn có đang sử dụng .id(), .equatable() hoặc tạo lại View trong body cha không.

.onDisappear có hoạt động trong SwiftUI trên macOS không?

Có, .onDisappear được hỗ trợ đầy đủ trên macOS (10.15+) với cùng hành vi: được gọi khi đóng cửa sổ, xóa bảng split view hoặc loại bỏ phương thức. Trên macOS, onDisappear cũng được gọi khi ẩn cửa sổ (không chỉ khi đóng), điều quan trọng cần xem xét cho các ứng dụng macOS.

Làm thế nào để hủy URLSessionTask trong .onDisappear?

Lưu tham chiếu đến URLSessionTask trong @State và gọi task.cancel() trong onDisappear. Thay vào đó, hãy sử dụng bộ điều chỉnh .task tự động hủy hoạt động không đồng bộ khi View biến mất. .task được ưu tiên cho tất cả các hoạt động không đồng bộ, bao gồm URLSession.

Tóm tắt

  • .onDisappear — bộ điều chỉnh cặp với onAppear để dọn dẹp tài nguyên và theo dõi khi View biến mất.
  • Thứ tự child-first — View con nhận onDisappear trước cha để có chuỗi dọn dẹp chính xác.
  • Không đảm bảo gọi — khi sự cố, thu nhỏ hoặc kết thúc hệ thống, onDisappear có thể không kích hoạt.
  • Các trường hợp sử dụng chính — hủy bộ đếm thời gian, đóng WebSocket, lưu trạng thái, theo dõi thời gian.
  • Phân tích — onDisappear gửi sự kiện screen_close với thời gian lưu lại được tính qua Date().timeIntervalSince.
  • Cho các hoạt động không đồng bộ — sử dụng .task thay vì onDisappear với Task {} vì .task tự động hủy yêu cầu.

Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay

IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.

Thảo luận dự án

Đọc thêm