Почти любое современное приложение не обходится без загрузки картинок из сети и мы, Surfingbird, не исключение. Однако, нельзя просто загружать картинки последовательно, потому что, если пользователь перемотает пару экранов, ему придётся ждать пока загрузятся предыдущие изображения, которые уже и не нужны.
Поэтому, для увеличения отзывчивости приложения и снижения времени ожидания пользователя, мы применили несколько приёмов, о которых и хотим сейчас рассказать.
Хочется отметить, что многие используют ленивую загрузку картинок. Это отличная практика, мы её любим и уважаем. Это экономия трафика и памяти на телефоне. Однако, чтобы позаботиться о памяти, важно не только организовать грамотное хранение загруженных изображений, например, вытесняющий кэш, но и оптимизировать саму загрузку.
Несмотря на то, что в современном мире у пользователей давно уже безлимитный интернет и трафик это не проблема, время ожидания загрузки изображений по прежнему критично. Пользователи очень нетерпеливые, они не хотят ждать загрузки, они хотят фыр-фыр-фыр и мы даём им эту магию.
Возьмем случай, когда приложение — это набор картинок. Как поступит разработчик, который не задумывается об отзывчивости приложения? Он возьмет все картинки, положит их в очередь на загрузку и окажется, что они будут загружаться, в среднем, одна за другой.
Посмотрите, как бы это могло выглядеть.
Что мы видим? Пользователю приходится ждать, пока загрузятся предыдущие картинки.
При этом, пользователь не видит, что что-то вообще происходит. Сейчас для пользователя приложение тормозит, а всё потому что нет никакой возможности понять – грузится картинка или нет.
Важно, чтобы у пользователя было понимание, что всё хорошо, всё грузится, ещё чуть-чуть и появится картинка. Первое что мы сделали — это добавили прогрессбар загрузки картинки. Реализовать это просто: добавляем сабвью к нашей картинке и меняем прогресс по колбеку:
// не надо относится к этому коду как к живому
// многие вещи типа логики показа/сокрытия progressView и тому подобные вещи скрыты
// код дан для того, чтобы было проще понять суть
-(void)layoutSubviews
{
CGRect rect = self.progressView.frame;
rect.size.width = self.bounds.size.width * self.progress;
self.progressView.frame = rect;
}
-(void)setProgress:(CGFloat)progress
{
_progress = progress;
if (_progress > 1)
_progress = 1;
if (_progress < 0)
_progress = 0;
[self setNeedsLayout];
}
И прописываем операции downloadProgressBlock (для операций, унаследованных от AFURLConnectionOperation):
[imageView.af_imageRequestOperation setDownloadProgressBlock:^(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead) {
weakSelf.progress = totalBytesRead/totalBytesExpectedToRead;
}];
Вот что из этого получается.
Несмотря на то, что ожидание загрузки картинки такое же, как и в первом случае, в итоге, у пользователя складывается ощущение, что всё хорошо.
Но это совсем не идеал. Как видно из примера, если пользователь перемотает совсем далеко, то ожидание загрузки картинки будет очень долгим. Так почему бы не показывать в первую очередь то, что на экране именно сейчас?
Для загрузки каждой картинки мы используем NSOperation. Как только мы получаем очередную порцию контента — создаем операцию загрузки для каждой картинки и помещаем её в очередь со средним приоритетом. Как только картинка оказывается в поле зрения — устанавливаем операции на ее загрузку высокий приоритет, и именно эта картинка грузится раньше тех, которые нам сейчас не нужны.
Есть еще одна тонкость — вероятнее всего, что те картинки, которые пользователь проскроллил, ему менее нужны, нежели те, которые внизу. Поэтому, как только картинка исчезает с экрана, мы устанавливаем операции на ее загрузку приоритет ещё меньший, нежели приоритет операций на предзагрузку.
- (void) willAppear
{
[self.af_imageRequestOperation setQueuePriority:NSOperationQueuePriorityVeryHigh];
}
- (void) willDisappear
{
[self.af_imageRequestOperation setQueuePriority:NSOperationQueuePriorityVeryLow];
}
В результате, нам удалось увеличить отзывчивость приложения и снизить время ожидания пользователя. Визуально приложение стало работать быстрее.
Если вы считаете, что подобные трюки не работают, советуем почитать это исследование фейсбука.
А вы применяете что-то подобное? Будет приятно обсудить в комментариях.