ViewController Lifecycle — este o secvență de metode pe care UIKit le apelează automat la gestionarea ecranelor în iOS. Conform Apple Documentation, fiecare UIViewController trece printr-un set previzibil de stări: de la crearea View până la apariția și ascunderea acesteia. Înțelegerea ordinii și scopului acestor metode este o condiție necesară pentru funcționarea stabilă a aplicației iOS.
Principalele
ViewController Lifecycle — este un set de metode pe care UIViewController le primește de la UIKit pe parcursul existenței sale. Fiecare ecran într-o aplicație iOS parcurge succesiv etapele de creare, încărcare a View, apariție pe ecran, dispariție și eliberare a memoriei. UIKit apelează automat metodele corespunzătoare la fiecare etapă, iar dezvoltatorul le suprascrie, adăugând propria logică.
Arhitectura UIViewController stă la baza UIKit și rămâne relevantă chiar și în era SwiftUI — multe proiecte încă folosesc abordarea clasică sau arhitectura hibridă. Înțelegerea Lifecycle permite prezicerea momentului când subviews sunt disponibile, când se poate modifica în siguranță layout-ul și ce operații să efectuezi la apariția sau ascunderea ecranului.
Fiecare metodă a ciclului de viață are un scop concret: unele sunt apelate o singură dată pe durata existenței controlerului, altele — la fiecare apariție sau dispariție. Amestecarea logicii între metode duce la bug-uri greu de depistat: scurgeri de memorie, actualizări incorecte ale datelor și cereri de rețea inutile.
Șase metode formează ciclul de viață complet al UIViewController. Ordinea apelării lor este fixă și nu depinde de modul de navigare — push, present sau unwind segue urmează aceeași programare.
loadView — prima metodă a ciclului, apelată când View-ul controlerului încă nu există. Dacă folosești Storyboard, UIKit încarcă automat View din fișierul xib. La crearea programatică a interfeței, suprascrii această metodă, atribuind manual View-ul rădăcină. În majoritatea proiectelor, loadView nu este modificat — munca se face în viewDidLoad.
Suprascrierea loadView este necesară doar în cazuri specifice: când întreaga interfață este creată prin cod fără Storyboard sau când View-ul rădăcină trebuie să fie dintr-o clasă nestandard. Apple recomandă să nu apelezi super.loadView la suprascriere — preiei complet crearea View-ului asupra ta.
override func loadView() {
view = UIView()
view.backgroundColor = .white
}
viewDidLoad — cea mai frecvent utilizată metodă a ciclului. Este apelată o singură dată după încărcarea View-ului în memorie, dar înainte de afișarea pe ecran. Aici se configurează subviews, se completează tabele cu date, se înregistrează celule și se abonează notificări care acționează pe toată durata vieții controlerului.
Caracteristică importantă: viewDidLoad nu este re-apelat la reafișarea ecranului. Dacă trebuie să actualizezi datele de fiecare dată la apariție — folosește viewWillAppear. În viewDidLoad plasează doar operații unice de care depinde configurarea de bază.
viewWillAppear este apelat de fiecare dată imediat înainte ca View-ul să devină vizibil pentru utilizator. Această metodă primește parametrul animated, care indică dacă apariția are loc cu animație. Aici se actualizează datele, se reîncarcă tabelele, se configurează NavigationBar și se ascund sau se arată elemente în funcție de starea aplicației.
Folosește viewWillAppear pentru sincronizarea stării între ecrane: dacă utilizatorul ar fi putut modifica datele pe ecranul anterior, această metodă este locul potrivit pentru actualizarea interfeței. Fiecare apel viewWillAppare precede apariția ecranului, chiar și la revenirea de la un controler copil.
viewDidAppear anunță că View-ul a apărut complet pe ecran și toate animațiile de tranziție s-au încheiat. În acest moment, ecranul este gata pentru interacțiune — utilizatorul vede interfața completă și poate lucra cu ea. Această metodă este potrivită pentru pornirea animațiilor care trebuie să înceapă după apariție, pornirea timerelor și urmărirea afișărilor de analitică.
Spre deosebire de viewWillAppear, viewDidAppear garantează că ecranul nu doar este vizibil, ci complet randat. Dacă pornești o animație în viewWillAppear, o parte din cadre pot fi omise, deoarece UIKit nu a finalizat încă tranziția. Pentru animații fluide, folosește viewDidAppear.
viewWillDisappear este apelat înainte de dispariția View-ului de pe ecran — la trecerea la un alt controler, închiderea ferestrei modale sau minimizarea aplicației. Acesta este locul potrivit pentru salvarea stării, dezabonarea de la notificări, oprirea proceselor active și eliberarea resurselor care nu sunt necesare când ecranul nu este vizibil.
Important de reținut: viewWillDisappear nu garantează că View-ul va dispărea în cele din urmă — gestul poate fi anulat. Prin urmare, salvează datele critice și în viewDidDisappear, care este apelat doar după dispariția efectivă.
viewDidDisappear încheie ciclul de apariție și dispariție. Este apelat după ce View-ul a fost deja ascuns de pe ecran. În această metodă se opresc definitiv animațiile, se șterg obiectele temporare și se confirmă salvarea datelor începută în viewWillDisappear.
Această metodă precede, de asemenea, deinit-ul controlerului — dacă UIViewController-ul tău este distrus, viewDidDisappear va fi ultima metodă Lifecycle înainte de apelul deinit. Folosește-o pentru curățarea finală care trebuie să aibă loc înainte de distrugerea obiectului.
Ordinea apelării depinde de modul exact în care apare ecranul: prima dată, la revenire sau la afișarea modală. Să analizăm trei scenarii principale din perspectiva UIKit.
La prima apariție a ecranului, UIKit parcurge ciclul complet de creare: se apelează loadView, apoi viewDidLoad, după care începe animația de apariție. În timpul animației se apelează viewWillAppear, iar după finalizare — viewDidAppear. Acesta este singurul scenariu în care toate metodele de la loadView la viewDidAppear sunt apelate succesiv.
override func viewDidLoad() {
super.viewDidLoad()
print("viewDidLoad — View încărcată în memorie")
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
print("viewWillAppear — va apărea în curând")
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
print("viewDidAppear — ecran complet vizibil")
}
Când utilizatorul revine la ecranul anterior, UIKit nu reapelează viewDidLoad — View-ul este deja încărcat în memorie. În schimb, pe ecranul de revenire se declanșează doar viewWillAppear și viewDidAppear, iar pe cel curent — viewWillDisappear și viewDidDisappear. loadView și viewDidLoad sunt omise, deoarece ecranul există deja în stiva de navigare.
Afișarea modală respectă aceleași reguli: la noul controler se apelează ciclul complet la prima apariție, iar la cel curent — viewWillDisappear și viewDidDisappear. La dismiss, ordinea este inversă: la controlerul care revine se declanșează din nou viewWillAppear și viewDidAppear, iar la cel ascuns — metodele finale. Acest comportament este uniform pentru toate tipurile de tranziții în UIKit.
Să analizăm patru scenarii cheie în care înțelegerea Lifecycle influențează direct calitatea codului și experiența utilizatorului. Pentru fiecare scenariu oferim un exemplu cu recomandări.
viewDidLoad — locul pentru configurarea primară care nu depinde de vizibilitatea ecranului. Aici se configurează collectionView, se înregistrează fișierele nib pentru celule, se creează data source și layout. Dacă încarci date din rețea, în viewDidLoad este mai bine doar să inițiezi cererea, iar interfața să o actualizezi în viewWillAppear, când ecranul este gata de afișare.
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(
MyCell.self,
forCellReuseIdentifier: MyCell.identifier
)
viewModel.loadInitialData()
}
Folosește viewWillAppear pentru sincronizarea datelor de fiecare dată la apariția ecranului. De exemplu, dacă utilizatorul ar fi putut modifica setările pe ecranul anterior, aici se actualizează valorile afișate, se reîncarcă tabela și se corectează starea NavigationBar. Aceasta garantează că ecranul arată întotdeauna date actualizate în orice scenariu de navigare.
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
tableView.reloadData()
navigationController?.setNavigationBarHidden(false, animated: animated)
}
viewDidAppear este ideal pentru pornirea animațiilor care trebuie să înceapă după ce utilizatorul a văzut ecranul. Aici se trimit și evenimentele de analitică: afișarea ecranului, pornirea onboarding-ului sau începerea redării video. Pornirea animațiilor înainte de finalizarea tranziției duce la o interfață sacadată — UIKit nu reușește să pregătească suficiente cadre.
În viewWillDisappear se salvează ciornele, se opresc timer-ele și se dezabonează de la NotificationCenter. Acesta este ultimul moment când ecranul este încă vizibil și disponibil pentru operații care necesită contextul utilizatorului. Pentru datele critice, suplimentar se folosește viewDidDisappear ca asigurare împotriva gesturilor anulate.
Utilizarea incorectă a metodelor ciclului de viață este una dintre cele mai frecvente surse de bug-uri în aplicațiile iOS. Să analizăm principalele greșeli pe care le fac dezvoltatorii în diferite etape de lucru cu UIViewController.
Prima greșeală — crearea subviews în init sau loadView atunci când se folosește Storyboard. Dacă folosești Interface Builder, nu suprascrie loadView fără necesitate. Crearea View-ului în loadView cu un storyboard existent duce la ignorarea fișierului xib și la un ecran gol.
A doua greșeală — abonarea la notificările de tastatură în viewDidLoad fără dezabonare. Dacă te-ai abonat la UIResponder.keyboardWillShowNotification dar nu te-ai dezabonat la ascunderea ecranului, blocul va fi apelat și după deinit-ul controlerului — aceasta este o scurgere de memorie cu potențială cădere a aplicației.
A treia greșeală — timer-e și cereri de rețea pornite înainte de apariția ecranului. Încărcarea imaginilor sau executarea animațiilor când View-ul nu este încă vizibil — o risipă de resurse. Mută actualizările vizuale în viewWillAppear sau viewDidAppear.
A patra greșeală — salvarea datelor doar în viewWillDisappear. La gestul interactiv pop, utilizatorul poate începe o glisare și o poate anula — metoda a fost apelată, dar ecranul nu a dispărut. Dublează salvarea critică în viewDidDisappear sau în handler-ul applicationDidEnterBackground.
Întrebări frecvente
O singură dată — după încărcarea View-ului în memorie. La reaparițiile ecranului, viewDidLoad nu este apelat. Dacă trebuie să re-creeze View, controlerul trebuie să fie distrus și creat din nou.
UIKit necesită apelarea super.viewDidLoad pentru funcționarea corectă a ciclului de viață. Fără el, pot apărea probleme cu actualizarea layout-ului și gestionarea tranzițiilor. Apelează întotdeauna super ca prima instrucțiune în metodă.
Nu este recomandat. Dacă controlerul este inițializat din Storyboard, UIKit încarcă automat View din xib. Suprascrierea loadView anulează acest proces, iar storyboard-ul tău va fi ignorat.
Abonează-te în viewDidLoad sau viewWillAppear și dezabonează-te în viewWillDisappear sau viewDidDisappear, folosind o referință slabă la self pentru a evita scurgerile de memorie în closure-uri.
Force quit omoară forțat procesul — UIKit nu apucă să apeleze metodele Lifecycle. Pentru salvarea datelor, folosește notificarea UIApplication.willTerminateNotification în AppDelegate.
Rezumat
Vom dezvolta o aplicație mobilă la cheie
IT Sectr creează aplicații iOS și Android pentru startup-uri și afaceri din 2017. Vă vom consilia și vă vom propune cea mai bună soluție.
Citiți și