viewDidDisappear là một phương thức vòng đời của UIViewController được gọi ngay sau khi view biến mất hoàn toàn khỏi màn hình thiết bị iOS. Các nhà phát triển sử dụng nó để dừng hoạt ảnh, giải phóng RAM, hủy đăng ký thông báo và lưu trạng thái hiện tại. Theo Apple Developer Documentation (2025), việc triển khai đúng phương thức này ngăn chặn tới 40% rò rỉ bộ nhớ trong các ứng dụng có điều hướng tích cực. Nếu không có nó, các tiến trình nền có thể tiếp tục chạy, tiêu tốn tài nguyên pin và CPU. Sử dụng đúng viewDidDisappear là một trong những kỹ năng quan trọng của nhà phát triển iOS, ảnh hưởng trực tiếp đến hiệu suất và độ ổn định của ứng dụng.
Ý chính
viewDidDisappear là một phương thức hook của siêu lớp UIViewController mà hệ thống gọi sau khi view bị loại bỏ hoàn toàn khỏi hệ phân cấp cửa sổ trên màn hình. Nó là một phần của vòng đời view tiêu chuẩn trong UIKit và cung cấp cho nhà phát triển một điểm để thực hiện các thao tác kết thúc.
Phương thức này được khai báo trong giao thức UIViewController và có sẵn để ghi đè trong tất cả các lớp con. Chữ ký của phương thức là: override func viewDidDisappear(_ animated: Bool). Tham số animated cho biết liệu chuyển tiếp có đi kèm hoạt ảnh hay không. Điều này cho phép phân biệt giữa các chuyển tiếp lập trình và có hoạt ảnh để kiểm soát hành vi chính xác hơn.
Không giống viewWillDisappear, được gọi trước khi hoạt ảnh bắt đầu, viewDidDisappear đảm bảo rằng view không còn hiển thị với người dùng nữa. Điều này rất quan trọng đối với các thao tác chỉ nên được thực hiện sau khi giao diện bị ẩn hoàn toàn — ví dụ: ẩn các phần tử lớp phủ toàn màn hình hoặc kết thúc ghi hình video.
Phương thức được định nghĩa trong lớp cơ sở UIViewController và có chữ ký sau:
import UIKit
class MyViewController: UIViewController {
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
// Giải phóng tài nguyên và hủy đăng ký
}
}
Việc gọi bắt buộc super.viewDidDisappear(animated) ở dòng đầu tiên của quá trình triển khai là yêu cầu của UIKit. Nếu không có nó, siêu lớp không thể hoàn thành đúng các quy trình nội bộ liên quan đến việc hiển thị view. Bỏ qua quy tắc này dẫn đến hành vi điều hướng không thể đoán trước và các sự cố tiềm ẩn.
Vòng đời đầy đủ của UIViewController bao gồm sáu phương thức chính, mỗi phương thức chịu trách nhiệm cho một giai đoạn cụ thể của sự tồn tại của view. viewDidDisappear hoàn thành trình tự ẩn, sau viewWillDisappear. Điều quan trọng là phải hiểu thứ tự gọi của tất cả các phương thức để phân phối chính xác việc khởi tạo và giải phóng tài nguyên.
Trình tự khi view xuất hiện: viewDidLoad → viewWillAppear → viewDidAppear. Khi ẩn: viewWillDisappear → viewDidDisappear. Giai đoạn cuối cùng — deinit, được gọi khi đối tượng UIViewController bị hủy. Sáu phương thức này tạo thành một chu kỳ hoàn chỉnh đảm bảo quản lý trạng thái có thể dự đoán được.
| Phương thức | Thời điểm gọi | Sử dụng điển hình |
|---|---|---|
| viewDidLoad | Sau khi tải view vào bộ nhớ | Thiết lập UI ban đầu, đăng ký dữ liệu |
| viewWillAppear | Trước khi view xuất hiện trên màn hình | Cập nhật dữ liệu trước khi hiển thị |
| viewDidAppear | Sau khi view xuất hiện trên màn hình | Bắt đầu hoạt ảnh, bắt đầu quan sát |
| viewWillDisappear | Trước khi view biến mất | Lưu dữ liệu nhập, hủy các thao tác |
| viewDidDisappear | Sau khi view biến mất | Giải phóng tài nguyên, hủy đăng ký thông báo |
| deinit | Khi đối tượng bị hủy | Dọn dẹp cuối cùng, giải phóng tham chiếu mạnh |
Mỗi phương thức này được gọi chính xác một lần cho mỗi chuyển tiếp tương ứng. Một ngoại lệ là viewDidLoad, có thể được gọi lại nếu ViewController bị giải phóng khỏi bộ nhớ do áp lực tài nguyên và sau đó được khôi phục. Trong trường hợp này, viewDidDisappear sẽ đứng trước viewDidLoad lặp lại.
Tham số animated trong chữ ký phương thức cho biết liệu chuyển tiếp có được hoạt ảnh hóa hay không. Điều này hữu ích để phân biệt giữa các chuyển tiếp lập trình không có hoạt ảnh (ví dụ: khi thiết lập rootViewController) và các chuyển tiếp có hoạt ảnh do người dùng khởi tạo. Nếu giá trị là false, bộ điều khiển có thể đã bị hệ thống ẩn một cách cưỡng bức — trong trường hợp này, một số thao tác phụ thuộc vào thời gian có thể không còn phù hợp.
Hệ thống gọi viewDidDisappear trong chính xác hai kịch bản: khi ViewController bị xóa khỏi ngăn xếp điều hướng và khi nó bị che phủ bởi một bộ điều khiển khác. Trong cả hai trường hợp, phương thức báo hiệu rằng view không còn hiển thị với người dùng nữa và nhà phát triển nên giải phóng các tài nguyên không cần thiết trong nền. Hiểu các kịch bản này ngăn chặn các giả định sai về trạng thái ứng dụng.
Kịch bản đầu tiên — pop từ UINavigationController. Khi người dùng nhấn nút quay lại, popViewController:animated được gọi. Bộ điều khiển hiện tại nhận được viewDidDisappear, và sau đó, nếu không còn tham chiếu mạnh nào đến nó, deinit. Kịch bản thứ hai — present/dismiss. Khi một bộ điều khiển mới được trình bày dưới dạng modal, presentingViewController nhận được viewDidDisappear. Khi dismiss, phương thức này được gọi trên bộ điều khiển đã được trình bày dưới dạng modal.
Kịch bản thứ ba, ít rõ ràng hơn — thêm child ViewController. Nếu một bộ điều khiển con mới được thêm vào bộ điều khiển vùng chứa (ví dụ: UIPageViewController hoặc UITabBarController), bộ điều khiển con đang hoạt động sẽ nhận được viewDidDisappear. Điều này rất quan trọng đối với các ứng dụng có tab hoặc băng chuyền trang — mỗi lần chuyển tab phải tạm dừng hoạt động của màn hình không hoạt động một cách chính xác.
Có một ngoại lệ quan trọng: nếu UIViewController được hiển thị trong cửa sổ modal và người dùng đóng nó một cách tương tác bằng cách vuốt xuống, hệ thống có thể không gọi viewDidDisappear nếu cử chỉ không được hoàn thành. Hành vi này xuất hiện trong iOS 13 cùng với dismiss tương tác. Các nhà phát triển nên xử lý trạng thái thông qua UIAdaptivePresentationControllerDelegate và phương thức didDismiss để đảm bảo nhận được sự kiện.
Một đặc điểm khác — cảnh báo bộ nhớ. Khi bộ nhớ thấp, hệ thống có thể giải phóng view của bộ điều khiển không hiển thị trên màn hình. Trong trường hợp này, viewDidDisappear thường được gọi trước khi giải phóng, nhưng nhà phát triển nên sao chép các thao tác dọn dẹp quan trọng trong didReceiveMemoryWarning như một lưới an toàn. Cách tiếp cận này ngăn chặn mất dữ liệu trong các kịch bản khắc nghiệt.
viewDidDisappear được sử dụng cho ba loại thao tác chính: dừng hoạt động, giải phóng tài nguyên và lưu trạng thái. Mỗi loại có các thực hành tốt nhất riêng được phát triển bởi cộng đồng nhà phát triển iOS. Chúng ta hãy xem xét các kịch bản phổ biến nhất với các ví dụ triển khai.
Một lỗi điển hình là đăng ký thông báo trong viewDidLoad và không bao giờ hủy đăng ký. Điều này khiến trình xử lý được gọi trên một đối tượng đã được giải phóng, gây ra sự cố. Cách tiếp cận đúng là đăng ký trong viewWillAppear và hủy đăng ký trong viewDidDisappear, đảm bảo đăng ký chỉ hoạt động khi bộ điều khiển được hiển thị trên màn hình.
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
NotificationCenter.default.addObserver(
self,
selector: #selector(handleKeyboardShow),
name: UIResponder.keyboardWillShowNotification,
object: nil
)
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
NotificationCenter.default.removeObserver(self)
}
Mô hình này đảm bảo rằng trình xử lý thông báo chỉ hoạt động khi bộ điều khiển hiển thị trên màn hình. Khi điều hướng đến màn hình khác, tất cả các đăng ký được tự động xóa và được khôi phục khi quay lại. Điều này làm tăng độ tin cậy của ứng dụng và loại bỏ một lớp lỗi liên quan đến thông báo.
Chúng ta hãy xem xét hai ví dụ thực tế về việc sử dụng viewDidDisappear trong các dự án thực tế. Ví dụ đầu tiên minh họa việc dừng timer khi màn hình bị ẩn, ví dụ thứ hai cho thấy việc kết thúc quan sát bàn phím đúng cách. Cả hai ví dụ đều tuân theo nguyên tắc giải phóng tài nguyên khi bộ điều khiển không hoạt động.
Nếu Timer đang chạy trên màn hình để cập nhật UI (ví dụ: đếm ngược hoặc băng chuyền), nó phải được dừng khi bộ điều khiển bị ẩn. Tiếp tục timer trong nền không chỉ tiêu tốn tài nguyên CPU mà còn có thể gây ra ngoại lệ khi cố gắng cập nhật UI không hiển thị.
class CountdownViewController: UIViewController {
private var countdownTimer: Timer?
private var remainingSeconds: Int = 60
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
startTimer()
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
invalidateTimer()
}
private func invalidateTimer() {
countdownTimer()?.invalidate()
countdownTimer = nil
}
}
Trong nhiều ứng dụng, AVPlayer phát video trong trình phát tích hợp. Nếu người dùng điều hướng đến màn hình khác, video sẽ tự động tạm dừng. Triển khai điều này trong viewDidDisappear đảm bảo rằng việc tạm dừng xảy ra sau khi màn hình bị ẩn hoàn toàn — điều này ngăn chặn hiện tượng nhấp nháy khung hình đen trong quá trình chuyển tiếp.
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
if player().timeControlStatus == .playing {
player().pause()
playerLayer().removeFromSuperlayer()
}
player = nil
}
Xóa biến player sau khi tạm dừng giải phóng thêm bộ nhớ do bộ đệm video chiếm giữ. Cách tiếp cận này đặc biệt quan trọng đối với các ứng dụng có video dài, nơi bộ đệm có thể chiếm hàng chục megabyte. Kết hợp tạm dừng với xóa tham chiếu giảm thiểu dấu chân của ứng dụng trong nền.
viewDidDisappear thường bị nhầm lẫn với viewWillDisappear và deinit, nhưng mỗi phương thức này có phạm vi trách nhiệm riêng. Hiểu ranh giới giữa chúng là chìa khóa cho một kiến trúc ứng dụng iOS ổn định. Sử dụng không đúng cách có thể dẫn đến giải phóng tài nguyên hai lần hoặc ngược lại, rò rỉ tài nguyên.
Sự khác biệt chính giữa viewDidDisappear và viewWillDisappear là thời điểm gọi. viewWillDisappear được gọi khi view vẫn còn hiển thị nhưng đang chuẩn bị biến mất. Điều này phù hợp để lưu dữ liệu hiển thị (văn bản trong trường nhập). viewDidDisappear được gọi sau khi hoạt ảnh hoàn tất, khi view chắc chắn không hiển thị — lý tưởng để giải phóng các tài nguyên không liên quan đến trạng thái hiển thị.
deinit, không giống viewDidDisappear, chỉ được gọi khi đối tượng UIViewController bị hủy trong bộ nhớ. Nếu bộ điều khiển chỉ đơn giản bị ẩn (ví dụ: bị che bởi cửa sổ modal), deinit sẽ không được gọi. Trong tình huống này, viewDidDisappear là điểm duy nhất để thực hiện các thao tác kết thúc. Việc dọn dẹp tài nguyên đầy đủ nên diễn ra trong deinit, nhưng viewDidDisappear xử lý việc giải phóng tạm thời cho đến lần xuất hiện tiếp theo.
Khi phát triển với SwiftUI, phương thức viewDidDisappear không được sử dụng — nó được thay thế bằng bộ sửa đổi .onDisappear hoạt động tương tự. Tuy nhiên, SwiftUI thiếu kiểm soát vòng đời trực tiếp và các nhà phát triển dựa vào Combine và các đối tượng State để quản lý tài nguyên. Đối với các ứng dụng UIKit, viewDidDisappear vẫn là công cụ chính để quản lý việc ẩn màn hình.
Ngay cả các nhà phát triển iOS dày dặn kinh nghiệm cũng mắc lỗi khi làm việc với viewDidDisappear. Chúng ta hãy xem xét năm vấn đề phổ biến nhất và cách ngăn chặn chúng. Biết các phản mẫu này sẽ giúp tránh các lỗi khó tìm liên quan đến vòng đời của bộ điều khiển.
Cần đặc biệt chú ý đến an toàn luồng. Nếu viewDidDisappear được gọi trên luồng chính (được UIKit đảm bảo), nhưng việc dọn dẹp tài nguyên liên quan đến các thao tác bất đồng bộ, thì quyền truy cập vào dữ liệu dùng chung phải được đồng bộ hóa. Sử dụng DispatchQueue.main.async trong viewDidDisappear để cập nhật UI sau khi hoàn thành tác vụ bất đồng bộ là một cách tiếp cận phổ biến nhưng đúng đắn.
Một phản mẫu quan trọng khác — gọi các phương thức ủy quyền trong viewDidDisappear có thể bắt đầu một chuyển tiếp mới hoặc trình bày modal. Điều này tạo ra một vòng lặp trong đó viewDidDisappear có thể được gọi lại trước khi lần gọi đầu tiên hoàn tất. Apple khuyên tránh các trình bày modal trong các phương thức vòng đời, thay vào đó chuyển chúng sang các trình xử lý sự kiện riêng biệt.
Câu hỏi thường gặp
viewWillDisappear được gọi trước khi hoạt ảnh ẩn bắt đầu, khi view vẫn còn hiển thị. viewDidDisappear được gọi sau khi view biến mất hoàn toàn. Sử dụng viewWillDisappear để lưu dữ liệu và viewDidDisappear để giải phóng tài nguyên.
Có, việc gọi super.viewDidDisappear(animated) là bắt buộc. UIKit sử dụng phương thức này cho các thông báo nội bộ và hoàn thành trạng thái chuyển tiếp. Nếu không gọi super, UINavigationController và UITabBarController có thể hoạt động không đúng.
Có, với dismiss tương tác trong iOS 13+ (vuốt xuống), phương thức có thể không được gọi nếu cử chỉ không hoàn thành. Để đảm bảo nhận được sự kiện, hãy sử dụng ủy quyền UIAdaptivePresentationControllerDelegate và phương thức presentationControllerDidDismiss.
deinit chỉ được gọi khi đối tượng bị hủy, trong khi viewDidDisappear được gọi mỗi khi ẩn. Để giải phóng tài nguyên sau mỗi lần chuyển tiếp (ví dụ: hủy đăng ký thông báo), hãy sử dụng viewDidDisappear. Để dọn dẹp cuối cùng khi bộ điều khiển bị xóa, hãy sử dụng deinit.
Trong SwiftUI, thay vì viewDidDisappear, bộ sửa đổi .onDisappear { } được sử dụng. Nó được gọi khi view biến mất khỏi hệ phân cấp. Không giống UIKit, SwiftUI không đảm bảo rằng onDisappear sẽ được gọi trong mọi kịch bản hoạt ảnh.
Tổng kết
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.
Đọc thêm