iOS-РАЗРАБОТКА
Плавная загрузка данных в Table View c помощью Uitableviewdatasource Prefetching
Предположим, что у вас есть View Controller с Table View и вы хотите загрузить в него 100 строк данных с помощью веб API. Как бы вы это сделали?

Данный материал предполагает, что у вас есть навыки работы с URLSession, JSON парсингом, делегатами, и c TableView.
2 августа 2018
Axel Kee
iOS Developer
Предположим, что у вас есть View Controller с Table View и вы хотите загрузить в него 100 строк данных с помощью веб API. Как бы вы это сделали?

Данный материал предполагает, что у вас есть навыки работы с URLSession, JSON парсингом, делегатами, и c TableView.
01
Загрузка всех 100 строк одновременно
Самый простой способ загрузки - это загрузить все данные одновременно с помощью URLSession сразу в viewDidLoad, а затем обновить Table View после полученного ответа.
Этот подход прост, но одновременное получение 100 строк и загрузка их в ячейки, может занять долгое время или вызвать всплеск в использовании памяти. А в некоторых случаях такой возможности и вовсе нет. Например, лента новостей Facebook: слишком много информации, чтобы уместить её в память iPhone, если загрузить все данные с момента создания вашей учетной записи.
02
Загрузка данных по частям, когда пользователь достигает конца страницы
Более продвинутый подход, это загрузка данных по частям: например, получить первые 20 строк, а затем, когда пользователь достиг конца страницы, еще 20. Это похоже на работу Facebook/Twitter, при загрузке ленты новостей
Код загрузки данных по частям обычно выглядит так:
Этот подход отлично работает и уменьшает нагрузку на память, поскольку он загружает только 15 элементов за один раз, и только тогда, когда пользователь достиг конца страницы. Единственный недостаток этого способа - пользователю приходится ждать загрузки следующей партии данных. А если мы можем сделать так, чтобы все строки сразу загружались непосредственно перед их отображением на экран? Не лучше ли, чтобы пользователь вообще не видел индикатор загрузки? ????
03
Введение в Prefetching UITableViewDataSource
Введение в
Prefetching UITableViewData-
Source
Протокол UITableViewDataSourcePrefetching для UITableView и UICollectionView стал доступен начиная с iOS10. Делегат функции tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) будет вызван, когда крайние строки будут рядом с областью отображения, вы можете вызвать веб API и загрузить их внутри этого же делегата функций.
Есть две функции в протоколе UITableViewDataSourcePrefetching:
Есть еще один метод схожий с tableView.dataSource из протокола UITableViewDataSource для Table View, с помощью которого можно хранить данные предварительной выборки - prefetchDataSource
04
Как работает предварительная загрузка
Это только мои личные наблюдения, поскольку Apple не упомянул точно, сколько строк за пределами видимой области будет предварительно определено.
  1. tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) не вызывается для строк, которые изначально видны на экране без скролла.

  2. Сразу после появления видимых строк, будет вызван tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]), а переменная indexPaths будет содержать в себе около 10 строк, которые находятся вблизи области отображения.

  3. В зависимости от скорости прокрутки, метод indexPaths будет содержать в себе различное количество строк. При нормальной скорости прокрутки обычно он содержит в себе 1 строку. Если вы прокручиваете страницу с бешенной скоростью, то количество строк будет больше.
Ниже есть видео, где показано как происходит вызов prefetchRowsAt и cancelPrefetchingForRowsAt метода для UITableViewDataSourcePrefetching.
05
Пример: Своевременная загрузка данных с помощью Prefetch
Для этого примера мы будем загружать 100 лучших новостей из Hacker News API c помощью Prefetch. Предположим, что у нас уже есть ID новостей из API Top Stories. Мы отобразим новости с помощью newsID на примере ниже:
В View Controller мы использовали массив для хранения объектов Новостей, полученного из API, его мы используем как источник данных для tableview. Также у нас есть другой массив, для отслеживания URLSessionDataTask, используемый для вызова API.
В viewDidload() мы создали источник данных для Table View и источник данных предвыборки для контроллера представления.
Вот функция для извлечения новостей из API. Мы будем получать данные путем создания URLSessionDataTask и после запуска этого задания, мы добавим их в массив dataTask, который определили ранее.
ELI 5: Optional, "self.newsTableView .indexPathsForVisibleRows? .contains(indexPath)" содержит в себе необязательную цепочку, потому что метод .indexPathsForVisibleRows? может вернуть nill, при отсутствии видимых строк в table view. Если он вернет значение nill, то вся и инструкция будет равна нулю. Значение ?? false является обратным коалесцированием nill, поэтому, если "indexPathsForVisibleRows? .contains(indexPath)" возвращает nill, то значение после двойных кавычек будет использовано как значение false.

После загрузки определенных новостей, если строка, которая должна отобразить их, находится в области видимых строк(в настоящее время она пустая), то мы обновим/перезагрузим эту строку и загрузим в неё данные.

Для оптимизации сетевых вызовов, мы добавим функцию cancelFetchNews, которая отменит dataTask, используемую для извлечения конкретных новостей, в момент прокрутки строки, которая должна показать эти новости. Эта функция будет использоваться в делегате метода cancelPrefetchingForRowsAt.
Теперь мы можем подключить функцию fetchNews в методе cellForRowAtIndexPath и , если ячейка на экране не имеет загруженных новостей, то мы запросим их через API.
configureCell -- это настраиваемый метод, который я написал, чтобы присваивать различные метки для ячеек с новостями. truncateCell просто устанавливает пустую метку.

Секретный ингредиент этой статьи - функция, которую мы назовем fetchNews. Она предназначена для получения строк с новостями, которые находятся рядом с видимой областью строк, но пока еще не видны. Таким образом, для пользователей создается иллюзия отсутствия загрузки????(данные загружаются до того, как строка появится на экране, только тссс).
Мы также вызываем cancelFetchNews делегат метода cancelPrefetchingForRowsAt, в момент, когда пользователь пролистывает от строки загрузки. Таким образом, мы оптимизируем сетевой вызов, отменив его, так как он в данный момент не нужен.

Конечный результат выглядит таким образом: помимо начальной загрузки, пользователь не заметит, что происходит какая-либо загрузка (надеюсь на это????). Это позволит сделать ее плавной:
Метод prefetch(предварительной выборки), который предоставляет Apple, позволяет нам лениво загружать данные. Загрузка происходит перед моментом, когда строка должна появиться на отображаемом экране. Это позволит улучшить пользовательский интерфейс, поскольку он не видит процесс загрузки(если он конечно не прокручивает как сумасшедший).

Конечно бывают случаи, когда невозможно использовать предвыборку из-за ограничений API HTTP, установленных backend разработчиками/компанией. Не бойтесь использовать методы, которые могут улучшить пользовательский опыт работы с Table View.

Документация Apple по UITableViewDataSourcePrefetching

На авторском курсе от Redmadrobot вы освоите азы iOS-разработки за пару месяцев и начнёте писать чистый код, соответствующий современным гайдам.

Учитесь у лучших — ведущие разработчики из Redmadrobot поделятся своими best practices, техниками и инструментами.
Прокачайтесь в iOS-разработке
Понравилось? Поделитесь с друзьями