viewDidLoad — е първият метод, който UIKit извиква, след като View на UIViewController бъде заредено в паметта. Според Apple Developer Documentation, този метод се извиква точно веднъж през цялото време на съществуване на контролера. viewDidLoad е основното място за начална конфигурация на интерфейса, регистрация на клетки и инициализация на данни.
Основни точки
viewDidLoad — е метод на инстанция на UIViewController, който UIKit извиква веднага след като View на контролера бъде заредено в RAM паметта. В този момент всички IBOutlet свойства вече са свързани с елементите на интерфейса, но View все още не е добавено в йерархията на прозорците и не е видимо за потребителя. Разработчикът презаписва този метод, за да извърши началната конфигурация на екрана.
Методът е част от ViewController Lifecycle и следва непосредствено след loadView, ако View се създава програмно, или след зареждане от Storyboard. В типичен проект viewDidLoad е най-често презаписваният метод на UIViewController, тъй като предоставя безопасна точка за работа с subviews, които вече съществуват и са готови за конфигурация.
Важен детайл: в момента на извикване на viewDidLoad размерите на View все още не съответстват на крайните — Auto Layout не е завършил преминаванията и frame може да се различава от очаквания. За изчисления, зависещи от размери, се използва viewDidLayoutSubviews.
Времето на извикване на viewDidLoad зависи от това как се инициализира контролерът. В повечето случаи UIKit автоматично извиква този метод при първия достъп до свойството view на контролера — това се нарича lazy-loading механизъм на UIViewController.
Когато NavigationController или TabBarController за първи път покаже вашия екран, UIKit проверява дали View е заредено. Ако не — извиква се loadView (или зареждане от Storyboard), след което незабавно се активира viewDidLoad. Това е стандартният сценарий и се случва еднократно за всяка инстанция на контролера.
override func viewDidLoad() {
super.viewDidLoad()
print("View е заредено — може да се конфигурира интерфейсът")
setupUI()
configureTableView()
}
viewDidLoad не се извиква отново при връщане към екрана чрез back button или dismiss. Ако вашата логика зависи от това, че екранът се появява отново — поставете я в viewWillAppear. Това е една от най-честите концептуални грешки: разработчиците очакват viewDidLoad да се активира при всяко показване, но UIKit го извиква само веднъж.
Понякога разработчиците принудително извикват view на контролера, за да стартират зареждането предварително: let _ = controller.view. Това форсира извикването на loadView и viewDidLoad, преди контролерът да се появи на екрана. Този трик се използва, когато трябва да подготвите View предварително за плавен преход.
viewDidLoad е предназначен за еднократни конфигурационни операции, които не зависят от това дали екранът е видим. Правилното използване на този метод е ключът към чиста архитектура и предвидимо поведение на контролера.
В viewDidLoad се регистрират nib файлове и класове за UITableView и UICollectionView, конфигурират се делегати и се задават начални стойности на свойствата на UI елементите. Тъй като всички IBOutlet са вече свързани в този момент, може безопасно да се осъществява достъп до label.text, imageView.image и други свойства на subviews.
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
tableView.delegate = self
tableView.register(
CustomCell.self,
forCellReuseIdentifier: CustomCell.identifier
)
title = "Главен екран"
}
Тук се създава viewModel, инициализира се data source с масиви и се абонират известия, които трябва да действат през целия живот на контролера. Например абонамент за UIApplication.willEnterForegroundNotification за актуализиране на данни при връщане от фон — подходящ кандидат за viewDidLoad. ViewModel в съвременната iOS архитектура служи като свързващо звено между контролера и бизнес логиката, а нейната инициализация точно в viewDidLoad осигурява готовност на данните в момента на първото появяване на екрана.
Обърнете специално внимание на конфигурацията на data source за таблици и колекции. Ако вашата таблица използва UIFetchedResultsController или NSFetchedResultsController с Core Data, инициализирайте fetch request и делегата в viewDidLoad. Това гарантира, че при първото появяване на екрана таблицата вече ще бъде попълнена с данни без допълнителни заявки.
В viewDidLoad се конфигурират бутоните на NavigationBar, настройва се large title, добавя се search controller и се задават бутоните edit/done. Тези елементи рядко се променят при повторно показване на екрана, така че тяхната инициализация тук е оптимална.
Не всички операции са подходящи за viewDidLoad. Някои действия, поставени в този метод, водят до прекомерна консумация на памет, неправилно поведение или грешки при повторно показване на екрана.
Избягвайте стартирането на мрежови заявки, чийто резултат засяга само UI. Ако заявката приключи преди появяването на екрана, потребителят няма да види резултата, а ако след — данните може да са остарели. Стартирайте зареждането в viewDidLoad, но актуализирайте UI в viewWillAppear.
Не изпълнявайте в viewDidLoad операции, зависещи от размерите и позицията на View. В момента на извикване Auto Layout не е завършил преминаванията и frame може да не е окончателен. За изчисления използвайте viewDidLayoutSubviews или презапишете updateViewConstraints.
Не се абонирайте за известия, които действат само когато екранът е видим. Известия за клавиатура, известия за промяна на съдържанието на подчинени контролери — абонирайте се за тях в viewWillAppear и отменете абонамента в viewDidDisappear, за да избегнете ненужни извиквания и изтичане на памет.
Не извиквайте методи, които изискват видим екран. Например опит за показване на UIAlertController от viewDidLoad ще причини грешка, тъй като View на контролера все още не е добавено в йерархията на прозорците. Всички UI операции, зависещи от window или presentedViewController, трябва да се изпълняват само след появяване на екрана.
Не инициализирайте тежки ресурси без необходимост. Ако екранът се отваря рядко или данните не се показват веднага, отложете създаването на ресурсоемки обекти до момента, в който наистина са необходими. Lazy инициализацията на свойства в Swift е вграден механизъм за решаване на тази задача: свойство с модификатор lazy ще бъде създадено едва при първия достъп, което спестява памет и ускорява зареждането на екрана.
Не използвайте viewDidLoad за операции, които трябва да се изпълняват при всяко появяване на екрана. Това е най-фундаменталната грешка: начинаещите разработчици често поставят логиката за актуализиране на данни в viewDidLoad и се чудят защо при връщане от друг екран таблицата не се презарежда. Ако операцията трябва да се повтаря при всяко показване — използвайте viewWillAppear. Ако трябва да се изпълни веднъж през живота — viewDidLoad. Запомнете това просто правило, за да избегнете повечето проблеми с жизнения цикъл на UIViewController.
Нека разгледаме три практически примера, които демонстрират правилното използване на viewDidLoad в реални проекти. Всеки пример решава конкретна задача за конфигурация на екрана.
override func viewDidLoad() {
super.viewDidLoad()
collectionView.register(
PhotoCell.self,
forCellWithReuseIdentifier: PhotoCell.reuseId
)
collectionView.register(
HeaderView.self,
forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader,
withReuseIdentifier: HeaderView.reuseId
)
viewModel.delegate = self
viewModel.fetchInitialPage()
}
override func viewDidLoad() {
super.viewDidLoad()
let label = UILabel()
label.text = "Здравей, свят!"
label.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(label)
NSLayoutConstraint.activate([
label.centerXAnchor.constraint(equalTo: view.centerXAnchor),
label.centerYAnchor.constraint(equalTo: view.centerYAnchor)
])
}
В viewDidLoad се конфигурират и елементи, показвани при липса на данни: празно състояние, loader, placeholder. Тези компоненти се създават веднъж и се използват повторно при всяко появяване на екрана. Скриването или показването на тези елементи се контролира в viewWillAppear в зависимост от актуалните данни.
override func viewDidLoad() {
super.viewDidLoad()
emptyStateLabel = UILabel()
emptyStateLabel.text = "Няма данни"
emptyStateLabel.textAlignment = .center
emptyStateLabel.isHidden = true
view.addSubview(emptyStateLabel)
activityIndicator = UIActivityIndicatorView(style: .medium)
activityIndicator.hidesWhenStopped = true
view.addSubview(activityIndicator)
}
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(
self,
selector: #selector(handleEnterForeground),
name: UIApplication.willEnterForegroundNotification,
object: nil
)
}
@objc private func handleEnterForeground() {
refreshContent()
}
Често задавани въпроси
При нормални условия не — UIKit извиква viewDidLoad веднъж след зареждане на View в паметта. Ако контролерът бъде унищожен и създаден наново, viewDidLoad ще работи за новата инстанция.
Да, задължително. Извикването на super.viewDidLoad гарантира, че UIKit ще извърши вътрешната конфигурация, необходима за правилната работа на Lifecycle. Винаги извиквайте super като първо нещо в метода.
viewDidLoad се извиква веднъж при зареждане на View. viewWillAppear се извиква всеки път преди появяване на екрана. Първият — за еднократна конфигурация, вторият — за актуализиране на данни и състояние.
Тежките синхронни операции в viewDidLoad блокират main thread и забавят появяването на екрана. Асинхронните зареждания са позволени, но при актуализиране на UI след завършването им трябва да се има предвид, че екранът може вече да е скрит.
Директно viewDidLoad не може да се извика — извиква го UIKit. За да форсирате зареждането на View, достъпете свойството controller.view. Това автоматично ще задейства loadView и viewDidLoad.
Обобщение
Ще разработим мобилно приложение под ключ
IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.
Прочетете също