ViewController Lifecycle — је низ метода које UIKit аутоматски позива при управљању екранима у iOS-у. Према Apple Documentation, сваки UIViewController пролази кроз предвидив скуп стања: од креирања View-а до његовог појављивања и скривања. Разумевање реда и намене ових метода је неопходан услов за стабилан рад iOS апликације.
Главно
ViewController Lifecycle — скуп метода које UIViewController прима од UIKit-а током свог постојања. Сваки екран у iOS апликацији сукцесивно пролази кроз фазе креирања, учитавања View-а, појављивања на екрану, нестајања и ослобађања меморије. UIKit аутоматски позива одговарајуће методе у свакој фази, а програмер их превазилази, додајући своју логику.
Архитектура UIViewController-а је основа UIKit-а и остаје актуелна чак и у ери SwiftUI-ја — многи пројекти и даље користе класични приступ или хибридну архитектуру. Разумевање Lifecycle-а омогућава предвиђање у ком тренутку су subviews доступни, када се безбедно може мењати layout и које операције извршавати при појављивању или скривању екрана.
Свака метода животног циклуса има конкретну намену: неке се позивају једном током постојања контролера, друге — при сваком појављивању или нестајању. Мешање логике између метода доводи до тешко уочљивих грешака: цурења меморије, нетачног ажурирања података и непотребних мрежних захтева.
Шест метода чине пун животни циклус UIViewController-а. Редослед њиховог позивања је фиксан и не зависи од начина навигације — push, present или unwind segue прате исти распоред.
loadView — прва метода циклуса, позива се када View контролера још не постоји. Ако користите Storyboard, UIKit аутоматски учитава View из xib датотеке. При програмском креирању интерфејса, превазилазите ову методу, ручно додељујући коренски View. У већини пројеката loadView се не дира — рад се обавља у viewDidLoad.
Превазилажење loadView-а је потребно само у специфичним случајевима: када се цео интерфејс креира кодом без Storyboard-а или када коренски View мора бити нестандардне класе. Apple препоручује да не позивате super.loadView при превазилажењу — потпуно преузимате креирање View-а на себе.
override func loadView() {
view = UIView()
view.backgroundColor = .white
}
viewDidLoad — најчешће коришћена метода циклуса. Позива се једном након што је View учитан у меморију, али још није приказан на екрану. Овде се подешавају subviews, попуњавају табеле подацима, региструју ћелије и претплаћују на обавештења која делују током целог живота контролера.
Важна карактеристика: viewDidLoad се не позива поново при поновном приказивању екрана. Ако треба да ажурирате податке сваки пут при појављивању — користите viewWillAppear. У viewDidLoad постављајте само једнократне операције од којих зависи основна конфигурација.
viewWillAppear се позива сваки пут непосредно пре него што View постане видљив кориснику. Ова метода прима параметар animated, који показује да ли се појављивање одвија са анимацијом. Овде се ажурирају подаци, поново учитавају табеле, конфигурише NavigationBar и скривају или приказују елементи у зависности од стања апликације.
Користите viewWillAppear за синхронизацију стања између екрана: ако је корисник могао да промени податке на претходном екрану, ова метода је право место за ажурирање интерфејса. Свако позивање viewWillAppear-а претходи појављивању екрана, чак и при повратку из подређеног контролера.
viewDidAppear обавештава да се View потпуно појавио на екрану и да су све анимације транзиције завршене. У овом тренутку екран је спреман за интеракцију — корисник види комплетан интерфејс и може да ради са њим. Ова метода је погодна за покретање анимација које треба да почну након појављивања, стартовања тајмера и праћења приказа аналитике.
За разлику од viewWillAppear-а, viewDidAppear гарантује да екран није само видљив, већ и потпуно рендерован. Ако покрећете анимацију у viewWillAppear-у, део кадрова може бити прескочен, јер UIKit још није завршио транзицију. За глатке анимације користите viewDidAppear.
viewWillDisappear се позива пре нестајања View-а са екрана — при преласку на други контролер, затварању модалног прозора или минимизацији апликације. Ово је право место за чување стања, одјављивање са обавештења, заустављање активних процеса и ослобађање ресурса који нису потребни када екран није видљив.
Важно је запамтити: viewWillDisappear не гарантује да ће View на крају нестати — гест може бити отказан. Због тога критичне податке чувајте и у viewDidDisappear-у, који се позива тек након стварног нестајања.
viewDidDisappear завршава циклус појављивања и нестајања. Позива се након што је View већ скривен са екрана. У овој методи се коначно заустављају анимације, бришу привремени објекти и потврђује чување података започето у viewWillDisappear-у.
Ова метода такође претходи deinit-у контролера — ако се ваш UIViewController уништава, viewDidDisappear ће бити последња метода Lifecycle-а пре позива deinit-а. Користите је за коначно чишћење које треба да се деси пре уништења објекта.
Редослед позивања зависи од тога како се тачно екран појављује: први пут, при повратку назад или при модалном приказу. Размотримо три главна сценарија из перспективе UIKit-а.
При првом појављивању екрана, UIKit пролази кроз пун циклус креирања: позивају се loadView, затим viewDidLoad, након чега почиње анимација појављивања. Током анимације позива се viewWillAppear, а након завршетка — viewDidAppear. Ово је једини сценариј у ком се све методе од loadView до viewDidAppear позивају сукцесивно.
override func viewDidLoad() {
super.viewDidLoad()
print("viewDidLoad — View учитан у меморију")
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
print("viewWillAppear — ускоро ће се појавити")
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
print("viewDidAppear — екран потпуно видљив")
}
Када се корисник врати на претходни екран, UIKit не позива поново viewDidLoad — View је већ учитан у меморију. Уместо тога, на повратном екрану се активирају само viewWillAppear и viewDidAppear, а на тренутном — viewWillDisappear и viewDidDisappear. loadView и viewDidLoad се прескачу, јер екран већ постоји у стеку навигације.
Модални приказ следи иста правила: код новог контролера позива се пун циклус при првом појављивању, а код тренутног — viewWillDisappear и viewDidDisappear. При dismiss-у редослед је обрнут: код повратног контролера поново се активирају viewWillAppear и viewDidAppear, а код скривеног — завршне методе. Ово понашање је јединствено за све типове транзиција у UIKit-у.
Размотримо четири кључна сценарија у којима разумевање Lifecycle-а директно утиче на квалитет кода и корисничко искуство. За сваки сценариј дајемо пример са препорукама.
viewDidLoad — место за почетно подешавање које не зависи од видљивости екрана. Овде се конфигурише collectionView, региструју nib датотеке за ћелије, креира data source и layout. Ако учитавате податке из мреже, у viewDidLoad је боље само иницирати захтев, а интерфејс ажурирати у viewWillAppear-у, када је екран спреман за приказ.
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(
MyCell.self,
forCellReuseIdentifier: MyCell.identifier
)
viewModel.loadInitialData()
}
Користите viewWillAppear за синхронизацију података сваки пут при појављивању екрана. На пример, ако је корисник могао да промени подешавања на претходном екрану, овде се ажурирају приказане вредности, поново учитава табела и коригује стање NavigationBar-а. Ово гарантује да екран увек приказује актуелне податке при било ком сценарију навигације.
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
tableView.reloadData()
navigationController?.setNavigationBarHidden(false, animated: animated)
}
viewDidAppear је идеалан за покретање анимација које треба да почну након што је корисник видео екран. Овде се такође шаљу догађаји аналитике: приказ екрана, почетак onboarding-а или почетак репродукције видеа. Покретање анимација пре завршетка транзиције доводи до трзавог интерфејса — UIKit не стиже да припреми довољан број кадрова.
У viewWillDisappear чувају се верзије радних докумената, заустављају тајмери и одјављује са NotificationCenter-а. Ово је последњи тренутак када је екран још видљив и доступан за операције које захтевају контекст корисника. За критичне податке додатно се користи viewDidDisappear као осигурање од отказаних гестова.
Неправилно коришћење метода животног циклуса је један од најчешћих извора грешака у iOS апликацијама. Размотримо основне грешке које програмери праве у различитим фазама рада са UIViewController-ом.
Прва грешка — креирање subviews у init-у или loadView-у при коришћењу Storyboard-а. Ако користите Interface Builder, не превазилазите loadView без потребе. Креирање View-а у loadView-у са постојећим storyboard-ом доводи до игнорисања xib датотеке и празног екрана.
Друга грешка — претплата на обавештења тастатуре у viewDidLoad без одјаве. Ако сте се претплатили на UIResponder.keyboardWillShowNotification, али се нисте одјавили при скривању екрана, блок ће се позивати и након deinit-а контролера — ово је цурење меморије са потенцијалним падом апликације.
Трећа грешка — тајмери и мрежни захтеви покренути пре појављивања екрана. Учитавање слика или извршавање анимација када View још није видљив — непотребно трошење ресурса. Преместите визуелна ажурирања у viewWillAppear или viewDidAppear.
Четврта грешка — чување података само у viewWillDisappear. При интерактивном pop гесту, корисник може да започне превлачење и да га откаже — метода је позвана, али екран није нестао. Дуплирајте критично чување у viewDidDisappear или у handler-у applicationDidEnterBackground.
Често постављана питања
Једном — након учитавања View-а у меморију. При поновном појављивању екрана viewDidLoad се не позива. Ако је потребно поново креирати View, контролер мора бити уништен и поново креиран.
UIKit захтева позив super.viewDidLoad за исправан рад животног циклуса. Без њега могући су проблеми са ажурирањем layout-а и обрадом транзиција. Увек позивајте super као прву ствар у методи.
Не препоручује се. Ако је контролер иницијализован из Storyboard-а, UIKit аутоматски учитава View из xib датотеке. Превазилажење loadView-а поништава овај процес и ваш storyboard ће бити игнорисан.
Претплатите се у viewDidLoad или viewWillAppear, а одјавите се у viewWillDisappear или viewDidDisappear, користећи слабу референцу на self да бисте избегли цурење меморије при затварањима.
Force quit присилно убија процес — UIKit не стиже да позове методе Lifecycle-а. За чување података користите обавештење UIApplication.willTerminateNotification у AppDelegate-у.
Резиме
Развићемо мобилну апликацију под кључ
IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође