«Искусство – это все, что вы можете сделать хорошо. Все, что вы можете сделать качественно» (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 (найти и заменить не так уж и сложно, поэтому не бойтесь), но если вы это сделали, то увидите, что:
- Вы должны присвоить ASTableNode свойству.
- У узла таблицы нет метода с именем -registerClass:forCellReuseIdentifier:.
- Вы не можете добавлять узел как 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;
};
}
Рассмотрим этот раздел:
- ASCellNode является ASDK эквивалентом UITableViewCell или UICollectionViewCell. Важно отметить, что этот метод возвращает ASCellNodeBlock. Это связано с тем, что ASTableNode поддерживает все свои ячейки внутри и предоставляет блок для каждого index path, он может одновременно инициализировать все свои ячейки, когда будет готов.
- Первое, что вы делаете – берете ссылку на модель данных, необходимую для заполнения этой ячейки. Это очень важная закономерность. Вы захватываете данные, а затем получаете их в следующем блоке. IndexPath не должен использоваться внутри блока, если данные изменяются до того, как блок был вызван.
- Затем вы возвращаете блок, возвращаемым значением которого должен быть ASCellNode.
- Не нужно беспокоиться о переиспользовании ячейки, расслабьтесь и инициализируйте её обычным способом. Заметьте, что теперь вы возвращаете 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:.
Давайте рассмотрим этот раздел:
- Во-первых, вы должны выполнить запрос, чтобы показать следующий пакет животных. Обычно это массив объектов, возвращаемых API.
- После завершения обновите таблицу с недавно загруженными данными.
- Наконец, удостоверьтесь, что вызвали -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;
}
Давайте рассмотрим этот раздел:
- Несмотря на то, что эта версия не является блочной, хорошей практикой является сначала захватить вашу модель данных.
- На этот раз вы используете мощный инициализатор -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 за помощь в переводе статьи.
Автор: PolyaLA