Комментарии 24
Особенно про расчет высоты ячеек.
по большей части, это имеет смысл при довольно объемном лайауте с обновлением ячеек или tableView.beginUpdates() / tableView.endUpdates(), в 99% случаем будет оверхед
В базе хранятся height, тип ячейки, нужные поля пред-обработанные. В общем, все что требуется для корректного отображения. Ширину можно не сохранять, если она фиксированная (кэп).
Даже без обновления это полезно, потому что меньше нагружается главный поток при скроллинге. Кроме банального быстродействия и экономии энергии, у вас появляется возможность накосячить с главным потоком где-нибудь в другом месте без последствий. Звучит немного странно, но на деле это ощутимо экономит время разработчика и снижает порог скилла для плавного интерфейса.
Достаточно считать все пропорционально экрану. А так как ваш iPhone 5 внезапно не превратится в X, то вычисленные значения будут актуальны между использованиями.
Если ваше приложение поддерживает ротацию, то сохраняйте два комплекта значений.
С точки зрения БД это можно организовать как угодно: сразу записывать в модель или сделать массив вспомогательных сущностей, которые дополнительно хранят информацию о критерии их применения, определяя в рантайме задействованный объект.
Разумеется, если приложение крайне динамическое, туда-сюда растягивается, то пред-расчет сделать сложнее или вообще не нужно. Так или иначе, каждая задача уникальна, и перед использованием того или иного подхода стоит проанализировать ситуацию.
Если что-то изменилось, то перерасчет в любом случае понадобится. В случае с Accessibility надо на уведомление всего лишь подписать, где и дергать нужный рычажок.
Говоря о сложном интерфейсе, то чем интереснее правила для лайаута, тем сильнее виден импакт на производительности. Это первый кандидат на автоматизацию.
У меня как-то был случай, когда ячейка и размер зависел от соседних ячеек. Сценарий распространялся на ситуацию, когда при удалении ячейки мог изменяться размер соседних. Уверен, вы представляете себе глубину норы. Все решилось тем же вызовом одной функции для перерасчета в нужный момент.
Хочу добавить, что у меня нет цели форсировать такое решение. Если вы считаете, что к вам это неприменимо — пожалуйста. Но я должен сказать, что оно имеет положительное влияние на скорость работы, а подавляющее большинство трудностей с ним несложно решаются.
Это мой практический опыт.
Встречался такой же сценарий за созависимость соседний ячеек от текущего размера и состояния, но тогда было удачное решение — производить некоторый расчет размеров внутри ViewModel, кэшировать эти размеры и отдавать в Collection View, и зачастую это зависит даже не от насыщенности UI эффектами, а бизнес логики работы общего контрола.
1. Вас не смущает что модель жестко привязана к UI?
2. Если дизайн выдаст экран с табличкой чуть уже остальных?
- Модель и хранилище — разные вещи, пусть и тесно связанные обычно. Мы не заморачиваемся разделением данных модели и данных UI, потому что они в любом случае будут вместе использоваться. Но если принципиально иметь разделенную модель и UI, то можно сделать отдельный инстанс базы для UI компонентов и оттуда уже запрашивать инфу.
- Разницы нет. Станет уже — значит это будет учтено в расчетах. А если вы имеете в виду, что делать, если уже есть посчитанные размеры из прошлой версии, то варианта два:
- В миграции учитывать этот момент.
- Просто очищать базу.
А дальше как сами сочтете нужным.
2. Вопрос в переиспользовании. Если ячейка используется в таблицах с разной шириной, или заголовок с разными шрифтами. Заводить разные записи в БД?
Не пробовали кстати замерять разницу между вычислением размеров по необходимости или запросы как базе? Неужели расчеты в tableView:heightForRowAtIndexPath и layoutSubviews настолько тяжеловесны что их лучше заменять базой данных?
- Тут есть drawback в виде быстродействия. Я бы не стал так заморачиваться, потому как выигрыша это никакого не дает. Да, разделено, идеологически красиво. А смысл? Вход в код не облегчает, масштабирование только усложняет. Хоть на первый взгляд так и кашернее, но на деле — нет.
- Разная ширина с разными шрифтами — это разные ячейки. Если это одна сущность, то делается массив UI компонентов модели, где у каждой есть критерий применения.
Как более простой для управления вариант — это на индивидуальные экраны создать отдельный инстанс базы. - Так как обычно обычно вычисляемые данные хранятся вместе с моделью, то фетчинг пары полей UI-ных вообще несущественен. Да даже если разделять, то все равно мелочь.
Представьте, что вам нужно посчитать высоту текста. Вы для этого создаете атрибутивную строку, что уже не мало так, считаете ее размер через CoreText/TextKit/UIKit, что вообще ахтунг, потому как работа с текстом на CPU в iOS — одна из тяжелейших операций. И делаете это на каждую ячейку, каждый раз, когда дергается heightForRow. Считайте сами.
А согли бы вы отдельный материал написать про подсчет высоты ячеек и последующее ранение/использование этих данных? Либо отправить на какой-то источник почитать.
Сейчас в процессе изучения swift и параллельного написания приложения рецептов для сайта.
И как паз столкнулся с тем, что когда в экране рецепта прокручиваешь пошаговый рецепт, то приложение скроллится с небольшим дерганием, как раз при обчислении высоты для attributed текста.
tableView:heightForRowAtIndexPath
в нем просчитывается высота. Как пример можно гуглить любой код связанный с таблицами без автоматического подсчета высоты. Автоматический подсчет советую не использовать от слова совсем, самый простой и эффективный способ получить лагающий список
На правах борца за истину — автоматический подсчет высот не всегда плох. Зависит от сложности ячейки и ее статичности.
Стоит всегда начинать с самого простого варианта, а усложнять по мере нарастания необходимости в оптимизации. Проще говоря, преждевременная оптимизация — это такое же зло, как и отсутствие оптимизации вовсе.
Материала такого не видел. Возможно, позже напишу.
Самый простой вариант — считаете высоту в момент получения данных о модели и сохраняете вместе с ней.
Спасибо, очень интересная и полезная статья, видео еще лучше)))
Autolayout не всегда достаточно производителен, особенно на сложных коллекциях и слабых устройствах. Поэтому приходится все делать ручками. Иногда достаточно снять с него ответственность за высоту, а иногда и целиком возвращаться к истокам с версткой на фреймах.
Порог допустимой производительности везде разный. Где-то даже один потерянный кадр бросится в глаза, а бывает, что и 30 фпс всех устраивает. Это нормальный сценарий, целиком зависящий от ключевой аудитории и бизнес модели.
Но если мы обсуждаем идеальную производительность без швов, когда приложение в любой момент времени выдает 60 фпс, то нужно проводить более объективный и структурированный анализ.
Что это значит? Когда речь заходит о лагах, каждый полагается на свое субъективное восприятие производительности. Если мы хотим убедиться, что приложение действительно способно выдерживать нагрузку, то следует подойти более научно к процессу:
- Ввести критерий 'лага'. Им является потеря кадра. Если нет возможности замерить объективно фпс, то альтернативной шкалой может являться нагрузка на главный поток: когда в некий момент времени она приближается к 100% (от 90% и выше). Это альтернативный критерий. Замечу, что нагрузка может вызвана не только вычислениями и графикой, а так же блокировкой и ожиданием.
- Определить пользовательские сценарии. Как правило, принятой формой проверки служит скроллинг в динамических коллекциях, имеющих постраничную загрузку (которая на швах не должна вызывать пробуксовывания) и динамический контент в ячейках (изображения или что-то требующее дополнительной подгрузки после появления).
Темп скроллинга должен быть замерен в нескольких ритмах:
- Плавный. Когда за один жест контент смещается не более, чем на экран. Естественно, при проведении жеста нужно отпускать экран для инерционного перемещения.
- Резкий. Например, несколько жестов подряд, когда данные смещаются на несколько экранов.
- Ищущий. Максимальная возможная скорость перемещения, можно без остановки. Такой сценарий случается, когда пользователь что-то пытается найти в контенте.
Каждый сценарий должен быть проведен на разных устройствах: нельзя замерять производительность только на десятом айфоне, например. Если во всех случаях приложение уверенно показывает 60 фпс и/или отсутствие предельной нагрузки на главный поток, а так же визуально движение ощущается плавным, то можно считать стресс-тест пройденным.
Последовательный анализ исключает все заявления типа: 'У меня аутолейат никогда не лагает, что я делаю не так'
Айфончик, не лагай. Часть 1: многопоточность для практиков