«Искусство – это все, что вы можете сделать хорошо. Все, что вы можете сделать качественно» (Robert M. Pirsig).


От переводчика:
С появлением autoLayout создавать интерфейс iOS-приложения стало намного проще и быстрее. Вам больше не нужно думать о подгонке размеров под определенные устройства, autoLayout сделает это за вас. Вычисление происходит на основе констрейнтов относительно ближайших элементов. Чем больше таких зависимостей, тем дольше будет строиться autoLayout, и это основная проблема всех приложений с сложным интерфейсом.
Использование AsyncDisplayKit позволит вам на порядок уменьшить объем работ, выполняемых в основном потоке, и реализовать сложные интерфейсы с минимальным количеством кода. Ребята из Raywenderlich сделали подробный и наглядный туториал по работе с ним. Надеюсь, что перевод статьи поможет вам ещё быстрее освоить этот фреймворк.

AsyncDisplayKit – это UI фреймворк, который был разработан и применен Facebook в приложении Paper. Он помог команде разработчиков Paper найти ответ на один из ключевых вопросов: как сохранить основной поток приложения максимально свободным.


Сейчас многие приложения основываются на пользовательском интерфейсе, который в значительной степени зависит от непрерывных движений и анимации на основе физики. По крайней мере, ваш UI, наверняка, базируется на каком-то из видов Scroll View.


Такие типы пользовательских интерфейсов полностью зависят от основного потока и чрезвычайно чувствительны к его тормозам. Нагруженный основной поток означает отброшенные фреймы и неприятный пользовательский опыт.


Основные факторы работы потока включают:


  • Measurement and Layout: такие события, как вызов метода heightForRowAtIndexPath: или sizeThatFits класса UILabel, а также экспоненциальная стоимость расчета констрейнтов.
  • Image Decoding: использование UIImage в режиме просмотра изображения означает, что данные изображения сначала должны быть декодированы.
  • Drawing: сложный текст, а также отрисовка градиентов и теней вручную.
  • Object Life Cycle: создание, управление и уничтожение системных объектов (т. е. cоздание UIView).

При правильном использовании AsyncDisplayKit позволяет выполнять все измерения, компоновку и рендеринг асинхронно по умолчанию. Без дополнительной оптимизации приложение может примерно на порядок уменьшить объем работ, выполняемых в основном потоке.


Кроме этих преимуществ в производительности, современный AsyncDisplayKit предлагает впечатляющий набор удобств для разработчиков, позволяющий реализовывать сложные интерфейсы с минимальным количеством кода.


В этом туториале AsyncDisplayKit 2.0, состоящем из двух частей, вы узнаете всё необходимое для создания полезного и динамического приложения с использованием ASDK.


Внимание: ASDK несовместим ни с Interface Builder, ни с AutoLayout, поэтому вы не будете использовать их. Хотя ASDK полностью поддерживает Swift (в отличие от ComponentKit), многие его пользователи все еще пишут на Objective-C. На момент создания этого туториала большинство из топ-100 бесплатных приложений вообще не содержало кода на Swift (хотя 6 использовали ASDK). Поэтому статья сосредоточена на Objective-C. Но есть и Swift версия образца проекта на случай, если вы ненавидите скобки.

Начнем


Для начала загрузите стартовый проект.
Проект использует CocoaPods, чтобы подключить AsyncDisplayKit. Итак, в обычном стиле CocoaPods, зайдите и откройте RainforestStarter.xcworkspace, но НЕ RainforestStarter.xcodeproj.


Примечание: Для работы с этим туториалом требуется соединение с интернетом.

Скомпилируйте и запустите приложение, состоящее из одной UITableView, содержащей список животных. Если вы посмотрите на код в AnimalTableController, то увидите, что это обычный класс UITableViewController, который вы, наверное, видели много раз.


Примечание: Убедитесь, что код запущен на реальном девайсе, а не на симуляторе.

Проскрольте таблицу и обратите внимание на количество фреймов, которые были пропущены. Не нужно запускать инструменты, чтобы увидеть, что это приложение нуждается в улучшении производительности.



Вы можете исправить это, используя AsyncDisplayKit.


Представляем ASDisplayNode


ASDisplayNode – базовый класс ASDK и, по сути, просто объект «view» в паттерне MVC, аналогично UIView или CALayer. Лучший способ представить ASDisplayNode – подумать об отношении между UIViews и CALayers, с которыми вы уже должны быть знакомы.


Помните, что на экране в iOS-приложении всё представлено через объект CALayer. UIViews создают и владеют поддержкой CALayer, к которому они добавляют сенсорную обработку и другие функции. Сам класс UIView не является подклассом CALayer. Вместо этого он оборачивает объект слоя, расширяя его функциональность.



В случае ASDisplayNode эта абстракция расширяется: вы можете думать о них как об обертывании представления так же, как представление заворачивает слой.
Что узлы приносят в таблицу кроме обычного представления, так это то, что они могут быть созданы и сконфигурированы в фоновых очередях и одновременно представлены по умолчанию.



К счастью, API для работы с узлами должен быть невероятно знакомым любому, кто использовал UIViews или CALayers. Все свойства view, которые вы обычно используете, доступны в эквивалентном классе узла. Вы можете получить даже доступ к самой вью или лееру, так же, как к .layer класса UIView.


Контейнеры узла


Хотя сами по себе узлы обеспечивают возможность значительного повышения производительности, настоящая магия происходит, когда они используются совместно с одним из четырех контейнерных классов.


К этим классам относятся:


  • ASViewController: подкласс UIViewController, который позволяет предоставить узел, которым вы хотите управлять.
  • ASCollectionNode и ASTableNode: эквиваленты узла UICollectionView и UITableView, подкласс которых поддерживается под капотом.
  • ASPagerNode: подкласс ASCollectionNode, который обеспечивает высокую производительность чтения по сравнению с UIPageViewController из фреймворка UIKit.


Если честно, настоящая магия исходит от ASRangeController, который использует каждый из этих классов, чтобы повлиять на поведение содержащихся узлов. А пока просто доверьтесь мне и запомните это на будущее.


Преобразование TableView


Первое, что вы должны сделать – это преобразовать текущее представление таблицы в узел таблицы. Это довольно просто.


Замена tableView на tableNode


Сначала перейдите к AnimalTableController.m. Добавьте следующую строку ниже других import в этом классе:


#import <AsyncDisplayKit/AsyncDisplayKit.h> 

Так вы импортируете ASDK, чтобы использовать фреймворк.
Затем перейдите и замените объявление свойства tableView:


@property (strong, nonatomic) UITableView *tableView;

на tableNode:


@property (strong, nonatomic) ASTableNode *tableNode;

Это приведет к тому, что большинство кода в этом классе сломается, но не паникуйте!



Серьезно, не переживайте. Эти ошибки и предупреждения будут служить вашим ориентиром при преобразовании того, что сейчас есть, в то, что вы хотите.


Ошибки в -viewDidLoad, конечно, связаны с тем, что tableView больше не существует. Я не собираюсь заставлять вас просматривать весь проект и изменять все экземпляры tableView на tableNode (найти и заменить не так уж и сложно, поэтому не бойтесь), но если вы это сделали, то увидите, что:


  1. Вы должны присвоить ASTableNode свойству.
  2. У узла таблицы нет метода с именем -registerClass:forCellReuseIdentifier:.
  3. Вы не можете добавлять узел как subview.

На этом этапе вы должны просто заменить -viewDidLoad на следующее:


- (void)viewDidLoad {
    [super viewDidLoad];

    [self.view addSubnode:self.tableNode];
    [self applyStyle];
}

Интересно, что вы вызываете -addSubnode: для UIView. Этот метод был добавлен ко всем UIViews через категорию и в точности эквивалентен:


[self.view addSubview:self.tableNode.view];

Затем исправьте -viewWillLayoutSubviews, замените реализацию этого метода на следующую:


- (void)viewWillLayoutSubviews {
    [super viewWillLayoutSubviews];
    self.tableNode.frame = self.view.bounds;
}

Все это заменяет self.tableView на self.tableNode для установки фрейма таблицы.
Затем найдите метод -applyStyle и замените реализацию следующим:


- (void)applyStyle {
    self.view.backgroundColor = [UIColor blackColor];
    self.tableNode.view.separatorStyle = UITableViewCellSeparatorStyleNone;
}

Строка, которая задает separatorStyle для таблицы – единственная измененная строка. Обратите внимание, как осуществляется доступ к свойству представления табличного узла, чтобы установить separatorStyle для таблицы. ASTableNode не раскрывает все свойства UITableView, поэтому вам нужно получить доступ к экземпляру UITableView базового узла таблицы, чтобы изменить определенные свойства UITableView.


Затем добавьте следующую строку в самом начале -initWithAnimals:


_tableNode = [[ASTableNode alloc] initWithStyle:UITableViewStylePlain];

И добавьте этот код в конец, перед оператором return инициализатора:


[self wireDelegation];

Так вы инициализируете AnimalTableController с узлом таблицы и вызовете -wireDelegation для подключения делегатов узла таблицы.


Настройка источника данных и делегата табличного узла


Как и UITableView, ASTableNode использует источник данных и делегат для получения информации о себе. Протоколы ASTableDataSource таблицы Table и ASTableDelegate очень похожи на UITableViewDataSource и UITableViewDelegate. Фактически, они определяют некоторые из тех же самых методов, таких как -tableNode: numberOfRowsInSection:. Два набора протоколов не соответствуют друг другу полностью, потому что ASTableNode ведет себя немного иначе, чем UITableView.


Найдите -wireDelegation и замените tableView на tableNode в реализации:


- (void)wireDelegation {
    self.tableNode.dataSource = self;
    self.tableNode.delegate = self;
}

Теперь вы получите warning, что AnimalTableController на самом деле не соответствует правильному протоколу. Сейчас AnimalTableController соответствует UITableViewDataSource и UITableViewDelegate. В следующих разделах вы будете принимать и реализовывать каждый из этих протоколов, чтобы узел таблицы мог функционировать.


Соответствие ASTableDataSource


В верхней части AnimalTableController.m найдите следующее объявление интерфейса категории DataSource:


@interface AnimalTableController (DataSource)<UITableViewDataSource>
@end 

И замените UITableViewDataSource на ASTableDataSource:


@interface AnimalTableController (DataSource)<ASTableDataSource>
@end

Теперь, когда объявление AnimalTableController соответствует ASTableDataSource, пришло время сделать это.


Перейдите в нижнюю часть AnimalTableController.m и найдите реализацию категории DataSource.
Сначала измените UITableViewDataSource метод -tableView: numberOfRowsInSection: на метод протокола ASTableDataSource, заменив его следующим.


- (NSInteger)tableNode:(ASTableNode *)tableNode numberOfRowsInSection:(NSInteger)section {
    return self.animals.count;
}

Далее ASTableNodes ожидает, что его ячейки будут возвращены по-другому, нежели ячейки UITableView. Чтобы приспособиться к новой парадигме, замените -tableView: cellForRowAtIndexPath: на следующий метод:


//1
- (ASCellNodeBlock)tableNode:(ASTableView *)tableView nodeBlockForRowAtIndexPath:(NSIndexPath *)indexPath {
    //2
    RainforestCardInfo *animal = self.animals[indexPath.row];

    //3
    return ^{
        //4
        CardNode *cardNode = [[CardNode alloc] initWithAnimal:animal];
        //Вы добавите здесь что-нибудь потом…
        return cardNode;
    };
}

Рассмотрим этот раздел:


  1. ASCellNode является ASDK эквивалентом UITableViewCell или UICollectionViewCell. Важно отметить, что этот метод возвращает ASCellNodeBlock. Это связано с тем, что ASTableNode поддерживает все свои ячейки внутри и предоставляет блок для каждого index path, он может одновременно инициализировать все свои ячейки, когда будет готов.
  2. Первое, что вы делаете – берете ссылку на модель данных, необходимую для заполнения этой ячейки. Это очень важная закономерность. Вы захватываете данные, а затем получаете их в следующем блоке. IndexPath не должен использоваться внутри блока, если данные изменяются до того, как блок был вызван.
  3. Затем вы возвращаете блок, возвращаемым значением которого должен быть ASCellNode.
  4. Не нужно беспокоиться о переиспользовании ячейки, расслабьтесь и инициализируйте её обычным способом. Заметьте, что теперь вы возвращаете CardNode вместо CardCell.

Это подводит меня к важному моменту. Как вы, возможно, уже заметили, при использовании ASDK нет переиспользования ячеек. Об этом уже дважды говорилось, но просто имейте это ввиду. Не бойтесь перейти в начало класса и удалить.


static NSString *kCellReuseIdentifier = @«CellReuseIdentifier»;

Вам больше это не нужно. Только подумайте. Вам никогда не придется волноваться о -prepareForReuse снова …


Соответствие ASTableDelegate


Перейдите в начало AnimalTableController.m и найдите следующее объявление интерфейса категории Делегата:


interface AnimalTableController (Delegate)@end

и замените UITableViewDelegate на ASTableDelegate:


interface AnimalTableController (Delegate)@end

Теперь, когда AnimalTableController принимает ASTableDelegate, пора перейти к реализации. Переместитесь к нижней части AnimalTableController.m и найдите реализацию этой категории Делегата.


Уверены, вы знаете, что с UITableView вам нужно обеспечить реализацию -tableView:heightForRowAtIndexPath:. Это происходит из-за того, что с UIKit, высота каждой ячейки вычисляется и возвращается делегатом таблицы.


ASTableDelegate не нуждается в -tableView:heightForRowAtIndexPath:. В ASDK все ASCellNodes ответственны за определение их собственного размера. Вместо того, чтобы обеспечивать статическую высоту, вы можете дополнительно определить минимальный и максимальный размер для своих ячеек. В данном случае нужно, чтобы каждая ячейка была как минимум так же высока, как 2/3 экрана.


Сейчас просто замените -tableView:heightForRowAtIndexPath: на:


- (ASSizeRange)tableView:(ASTableView *)tableNode 
    constrainedSizeForRowAtIndexPath:(NSIndexPath *)indexPath {
    CGFloat width = [UIScreen mainScreen].bounds.size.width;
    CGSize min = CGSizeMake(width, ([UIScreen mainScreen].bounds.size.height/3) * 2);
    CGSize max = CGSizeMake(width, INFINITY);
    return ASSizeRangeMake(min, max);
}

Скомпилируйте и запустите проект, чтобы увидеть, что получилось.


У нас получилась плавная таблица! Как только вы сделали это, подготовьтесь сделать еще лучше.


Бесконечный скролл с пакетной выборкой


В большинстве приложений сервер имеет больше возможных данных, чем количество ячеек, которые вы хотели бы показать в таблице. Это означает, что в каждом приложении, над которым вы работаете, будет установлен механизм для загрузки следующего пакета данных от сервера, потому что пользователь приближается к концу текущего набора данных.


Много раз это обрабатывалось вручную, запоминая смещение контента в делегате scrollView в методе -scrollViewDidScroll:. С ASDK есть более декларативный способ сделать это. Вы можете сказать заранее, сколько страниц нужно для загрузки новых данных.


Сперва раскомментируйте вспомогательные методы, которые были включены. Перейдите в конец AnimalTableController.m и раскомментируйте эти два метода в категории Helpers. Вы можете считать -retrieveNextPageWithCompletion: сетевым вызовом, в то время как -insertNewRowsInTableNode – стандартный метод для добавления новых элементов в таблицу.


Затем добавьте следующую строку к -viewDidLoad:.


self.tableNode.view.leadingScreensForBatching = 1.0; // overriding default of 2.0

Установка leadingScreensForBatching в 1.0 означает, что вы хотите, чтобы новые пакеты были загружены каждый раз, когда пользователь прокрутил к точке, где до конца таблицы остался лишь 1 элемент.


Теперь добавьте следующий метод к реализации категории Делегата:


- (BOOL)shouldBatchFetchForTableNode:(ASTableNode *)tableNode {
    return YES;
}

Этот метод нужен для того, чтобы сказать таблице, должна ли она продолжить выполнять запросы для загрузки новых пакетов. Если вы знаете, что загружать больше нечего – верните NO.
Если вы действительно хотите, чтобы эта таблица скроллилась бесконечно, просто верните YES, чтобы гарантировать, что всегда будут запрашиваться новые пакеты.


Затем также добавьте:


- (void)tableNode:(ASTableNode *)tableNode willBeginBatchFetchWithContext:(ASBatchContext *)context {
    //1
    [self retrieveNextPageWithCompletion:^(NSArray *animals) {
        //2
        [self insertNewRowsInTableNode:animals];

        //3
        [context completeBatchFetching:YES];
    }];
}

Этот метод вызовется, когда пользователь приблизится к концу таблицы, и таблица получит YES от -shouldBatchFetchForTableNode:.


Давайте рассмотрим этот раздел:


  1. Во-первых, вы должны выполнить запрос, чтобы показать следующий пакет животных. Обычно это массив объектов, возвращаемых API.
  2. После завершения обновите таблицу с недавно загруженными данными.
  3. Наконец, удостоверьтесь, что вызвали -completeBatchFetching: с YES. Запросы на получение новых данных не будут выполнены, пока предыдущий не был завершен.
    Скомпилируйте, запустите и начните скролить. Не останавливайтесь, пока вы не увидите другую птицу. Они бесконечны.


Умный preloading


Вы когда-нибудь пробовали загружать контент заранее в приложении с помощью scrollView или PageViewController? Возможно, вы работали над полноэкранной фото галереей и захотели, чтобы следующие несколько изображений были загружены и ожидали, а пользователи редко видели placeholder.



Когда вы работаете над такой системой, как эта, начинаете осознавать, что есть много вещей, о — которых стоит подумать.


  • Сколько памяти вы занимаете?
  • Как много информации нужно загружать заранее?
  • Когда вы решаете вывести ответ пользователям?
    И задача становится намного сложнее, когда у вас контент разного размера. У вас есть PageViewController с CollectionView внутри каждого контроллера? Теперь вы должны подумать о том, как динамично загружать контент в обоих направлениях … И настроить отображение для каждого устройства, которое поддерживаете.


В каждом из контейнерных классов есть понятие состояния интерфейса для каждого из узлов. В любой момент времени узел может быть в любой комбинации:



  • Preload Range: Обычно дальний диапазон от видимого. Когда контент для каждого подузла в ячейке, такого как ASNetworkImageNode, должен быть загружен из внешнего источника; из API или локального кэша, например. Это контрастирует с пакетной выборкой, которая должна использоваться для выборки объектов моделей, представляющих сами ячейки.
  • Display Range: Здесь происходят отрисовка текста и декодирование изображения.
  • Visible Range: В этом диапазоне узел на экране занимает по крайней мере один пиксель.

Эти диапазоны также работают с метрикой «screenfuls» и могут быть легко настроены с помощью свойства ASRangeTuningParameters.


Например, вы используете ASNetworkImageNode для отображения изображения на каждой странице галереи. Каждый узел запрашивает данные из сети, когда входит в диапазон предварительной загрузки, и декодирует изображение, которое было извлечено, когда оно входит в диапазон отображения.


На самом деле, вы не должны постоянно думать об этих диапазонах, если не хотите. Встроенные компоненты, такие как ASNetworkImageNode и ASTextNode, в полной мере используют их. Это означает, что вы увидите преимущества по умолчанию.


Примечание: Одна вещь, которая может быть не очевидной, состоит в том, что эти диапазоны не складывают. Вместо этого они накладываются и сходятся на видимом диапазоне. Если вы установите дисплей и выберете оба диапазона на один экран, они проявятся в одно и то же время. Эти данные обычно нужны для отображение на экране, поэтому обычно диапазон упреждающей выборки должен быть немного больше, чтобы узлы были готовы запустить процесс дисплея, когда они доберутся до этого диапазона.
В целом, ведущая сторона диапазона больше, чем конечная. Когда пользователь изменяет направление скролла, размеры диапазонов тоже меняются, чтобы подготовиться к контенту, к которому двигается пользователь.

Интерфейс узла в состоянии обратного вызова


Возможно, вам интересно, как эти диапазоны правильно работают? Хорошо, что спросили.
У каждого узла в системе есть interfaceState свойство, которое является типом «битового поля» (NS_OPTION) ASInterfaceState. Поскольку ASCellNode перемещается через представление прокрутки, которым управляет ASRangeController, каждый подузел обновляет interfaceState свойство. Это значит, что даже самые глубокие узлы в дереве могут реагировать на изменения interfaceState.


К счастью, редко приходится возиться с битами interfaceState узла напрямую. Чаще всего вы просто хотите отреагировать на переход узла в определенное состояние или выход из него. Вот тут-то и появляются обратные вызовы состояния интерфейса.


Наименование узлов


Чтобы увидеть, как узел переходит из одного состояния в другое, полезно дать ему имя. Таким образом, вы сможете наблюдать, как каждый узел загружает свои данные, отображает их содержимое, появляется на экране, а затем делает всё это наоборот, когда исчезает.
Вернитесь к -tableNode:nodeBlockForRowAtIndexPath: и найдите комментарий, в котором говорится:


//Вы добавите здесь что-нибудь потом…

Ниже добавьте следующую строку, чтобы дать каждой ячейке debugName.


cardNode.debugName = [NSString stringWithFormat:@«cell %zd», indexPath.row];

Теперь вы сможете отслеживать перемещение ячеек через диапазоны.


Наблюдение за ячейками


Перейдите в CardNode_InterfaceCallbacks.m. Здесь вы найдете шесть методов, которые сможете использовать для отслеживания прогресса узла через различные диапазоны. Раскомментируйте их, а затем скомпилируйте и запустите. Удостоверьтесь, что ваша консоль в Xcode видна, и затем медленно прокручивайте. Смотрите, как различные ячейки реагируют на свои изменяющиеся состояния.



Примечание: В большинстве случаев единственным способом изменения ASInterfaceState, который вам нужен, является метод -didEnterVisibleState или -didExitVisibleState.
Основная реализация скрыта от вас. Чтобы проверить, что вы можете сделать, интегрировав состояния Preload и Display, посмотрите на код в ASNetworkImageNode. Все узлы сетевых изображения будут автоматически извлекать и декодировать свое содержимое, а также освобождать память, без необходимости двигать пальцем.

(Интеллектуальный Preloading)2


В версии 2.0 была введена концепция интеллектуальной предварительной загрузки контента по нескольким направлениям. Скажем, у вас есть вертикально прокручиваемое представление таблицы, и в какой-то момент на экране появляется ячейка, содержащая горизонтальный вид коллекции.



Хотя эта коллекция сейчас технически находится в видимой области, вам не хотелось бы загружать всю коллекцию заранее. Вместо этого оба вида прокрутки имеют свой собственный ASRangeController с отдельными настраиваемыми параметрами настройки диапазона.


Ввод второй размерности


Теперь, когда вы завершили AnimalTableController, вы можете использовать его как страницу в ASPagerNode.


Контроллер представления, который вы будете использовать для размещения этого пейджера, уже находится в проекте, поэтому первое, что вам нужно сделать, это перейти в AppDelegate.m.
Найдите -installRootViewController и замените:


AnimalTableController *vc = [[AnimalTableController alloc] 
                              initWithAnimals:[RainforestCardInfo allAnimals]];

на:


AnimalPagerController *vc = [[AnimalPagerController alloc] init];

Затем перейдите в AnimalPagerController.m и добавьте следующие строки в инициализатор, прямо перед оператором return. Все, что вам нужно сделать, это создать новый пейджер и установить его dataSource в качестве этого контроллера представления.


_pagerNode = [[ASPagerNode alloc] init];
_pagerNode.dataSource = self;

Узел пейджера фактически является подклассом ASCollectionNode, предварительно сконфигурированным для использования таким же образом, как и UIPageViewController. Хорошо, что думать об API на самом деле немного проще, чем о UIPageViewController.


Следующее, что вам нужно сделать, это реализовать методы источника данных пейджера. Перейдите к реализации категории ASPagerDataSource в конец этого файла.


Сначала скажите пейджеру, что число его страниц равно количеству массивов животных, в данном случае, три, заменив существующий -numberOfPagesInPagerNode:.


- (NSInteger)numberOfPagesInPagerNode:(ASPagerNode *)pagerNode {
    return self.animals.count;
}

Затем вам нужно реализовать -pagerNode:nodeAtIndex: аналогично методу источника данных узла, который вы ранее реализовали для ASTableNode.


- (ASCellNode *)pagerNode:(ASPagerNode *)pagerNode nodeAtIndex:(NSInteger)index {
    //1
    CGSize pagerNodeSize = pagerNode.bounds.size;
    NSArray *animals = self.animals[index];

    //2
    ASCellNode *node = [[ASCellNode alloc] initWithViewControllerBlock:^{
       return [[AnimalTableController alloc] initWithAnimals:animals];
    } didLoadBlock:nil];

    return node;
}

Давайте рассмотрим этот раздел:


  1. Несмотря на то, что эта версия не является блочной, хорошей практикой является сначала захватить вашу модель данных.
  2. На этот раз вы используете мощный инициализатор -initWithViewControllerBlock:. Все, что вам нужно – это вернуть блок, возвращающий контроллер узла таблицы, который вы исправили ранее, и управляемое представление будет автоматически использоваться в качестве представления для каждой страницы.

После добавления этого метода, у вас будет полностью функционирующий пейджер, ячейки которого будут сгенерированы из созданного ранее tableNodeController. Это полностью поставляется с двухмерной предварительной загрузкой, основанной на вертикальной и горизонтальной прокрутке, выполняемой пользователем!



Куда идти дальше?


Полную версию проекта для этого туториала по AsyncDisplayKit 2.0 можно скачать здесь. У нас также есть вариант на Swift.


Скоро будет готов перевод 2 части этого проекта. Она позволит узнать о новой, мощной системе компоновки, представленной в AsyncDisplayKit 2.0.


Можете провести небольшое исследование, прежде чем двигаться дальше: откройте домашнюю страницу AsyncDisplayKit и прочитайте часть документации. У Скотта Гудсона (Scott Goodson, оригинального автора AsyncDisplayKit) также есть несколько лекций, которые могут вас заинтересовать, самые новые из которых дают хороший обзор нескольких больших проблем с изображениями, которые фреймворк пытается решить.


Возможно, вам будет интересно Building Paper. Хотя в то время все это не было open sourse, и многое изменилось, довольно интересно посмотреть, с чего все это начиналось.


Наконец, сообщество AsyncDisplayKit приветствует вновь прибывших. Есть общедоступный канал в Slack, в который любой может вступить и задать вопросы.


Надеемся, вам понравился этот туториал, сообщите, есть ли у какие-либо вопросы или комментарии, присоединившись к обсуждению на форуме.


От переводчика:
В апреле разработчики переименовали фреймворк. Теперь он называется Texture. Подробнее можно прочитать тут.


P.S. Отдельное спасибо BeataSunshine и evgen за помощь в переводе статьи.

Поделиться с друзьями
-->

Комментарии (2)


  1. RedRover
    23.05.2017 11:17

    Причина тормозов стартового проекта (в статье ссылка не правильная но из оригинала ее можно взять) вызов функции заблюривания в главном потоке. Стоит делать это в фоне и все начнет летать без применения AsyncDisplayKit'а.
    Т.е. по сути тут решается проблема плохой архитектуры за счет подключения тяжеловесной библиотеки.


    1. PolyaLA
      24.05.2017 10:21

      Спасибо, поправила ссылку. Конкретно в этом случае да, но в реальных проектах часто используются кастомные ячейки со сложным UI, на отрисовку которого требуется много времени.