Щоб знати, коли подання таблиці закінчується завантаженням його вмісту, спершу потрібно мати базове розуміння того, як представлення зображень виводяться на екран.
У життєвому циклі програми є 4 ключові моменти:
- Додаток отримує подію (сенсорний, таймер, пересилається блок тощо)
- Додаток обробляє подію (це змінює обмеження, запускає анімацію, змінює фон тощо)
- Додаток обчислює нову ієрархію перегляду
- Додаток відображає ієрархію перегляду та відображає його
2 і 3 рази повністю розділені. Чому? З міркувань продуктивності ми не хочемо виконувати всі обчислення моменту 3 щоразу, коли відбувається модифікація.
Отже, я думаю, що ви стикаєтесь із таким випадком:
tableView.reloadData()
tableView.visibleCells.count // wrong count oO
Що тут не так?
Як і будь-який вигляд, подання таблиці ліниво перезавантажує його вміст. Насправді, якщо ви телефонуєте reloadData
кілька разів, це не створюватиме проблем із продуктивністю. Перегляд таблиці лише перераховує розмір вмісту, виходячи з його виконання делегата, і чекає моменту 3, щоб завантажити його комірки. Цей час називається пропускним макетом.
Гаразд, як потрапити в пропуск макета?
Під час проходу макета додаток обчислює всі кадри ієрархії перегляду. Для того, щоб в ньому участь, ви можете перевизначити спеціальні методи layoutSubviews
, і updateLayoutConstraints
т.д. в UIView
і еквівалентні методи в вид контролера підкласу.
Саме це і робить подання таблиці. Він переосмислює layoutSubviews
і на основі вашої реалізації делегата додає або видаляє комірки. Він дзвонить cellForRow
прямо перед додаванням та викладкою нової комірки, willDisplay
відразу після. Якщо ви зателефонували reloadData
або просто додали подання таблиці до ієрархії, подання таблиць додасть стільки комірок, скільки потрібно, щоб заповнити його кадр у цей ключовий момент.
Добре, але тепер, як дізнатися, коли подання таблиць закінчило перезавантаження свого вмісту?
Тепер ми можемо перефразувати це запитання: як дізнатися, коли подання таблиці закінчилося, викладаючи свої підгляди?
• Найпростіший спосіб - потрапити в макет подання таблиці:
class MyTableView: UITableView {
func layoutSubviews() {
super.layoutSubviews()
// the displayed cells are loaded
}
}
Зауважимо, що цей спосіб називається багато разів у життєвому циклі подання таблиці. Через прокручування та поведінку декею в поданні таблиці клітинки часто змінюються, видаляються та додаються. Але це працює, відразу після super.layoutSubviews()
завантаження комірок. Це рішення еквівалентно дочекатися willDisplay
події останнього шляху індексу. Ця подія викликається під час виконання подання layoutSubviews
таблиці для кожної доданої комірки.
• Ще один спосіб викликати, коли додаток закінчує пропуск макета.
Як описано в документації , ви можете використовувати опцію UIView.animate(withDuration:completion)
:
tableView.reloadData()
UIView.animate(withDuration: 0) {
// layout done
}
Це рішення працює, але екран буде оновлюватися один раз між часом, коли зроблено макет, і тим часом, коли викликається блок. Це еквівалентно DispatchMain.async
рішенню, але зазначено.
• Крім того, я вважаю за краще змусити викласти макет подання таблиці
Існує спеціальний метод, щоб змусити будь-який вид негайно обчислити його кадри підзагляду layoutIfNeeded
:
tableView.reloadData()
table.layoutIfNeeded()
// layout done
Але будьте обережні, але це усуне ледачу завантаження, яку використовує система. Виклик цих методів неодноразово може створювати проблеми з продуктивністю. Переконайтеся, що вони не будуть викликатись до обчислення кадру подання таблиці, інакше подання таблиці буде завантажено знову, і вам не надійде сповіщення.
Я думаю, що ідеального рішення немає. Класи підкласифікації можуть призвести до величини. Пропуск макета починається зверху і йде вниз, тому отримувати сповіщення, коли зроблено все компонування, непросто. І layoutIfNeeded()
може створити проблеми з продуктивністю тощо. Але знаючи ці варіанти, ви повинні мати можливість продумати одну альтернативу, яка відповідає вашим потребам.