AsyncDisplayKit 2.0 Tutorial: Getting Started
In this AsyncDisplayKit 2.0 tutorial, learn how to make your user interfaces scroll as smooth as butter through the power of asynchronous rendering. By Luke Parham.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
AsyncDisplayKit 2.0 Tutorial: Getting Started
30 mins
- Getting Started
- Introducing ASDisplayNode
- The Node Containers
- Converting the TableView
- Replacing tableView with tableNode
- Setting the Table Node’s Data Source & Delegate
- Conforming to ASTableDataSource
- Conforming to ASTableDelegate
- Infinite Scrolling with Batch Fetching
- Intelligent Preloading
- Node Interface State Callbacks
- Naming Nodes
- Observing the Cells
- (Intelligent Preloading)2
- Entering the Second Dimension
- Where To Go From Here?
Setting the Table Node’s Data Source & Delegate
Just like UITableView
, ASTableNode
uses a data source and delegate to get information about itself. Table node’s ASTableDataSource
and ASTableDelegate
protocols are very similar to UITableViewDataSource
and UITableViewDelegate
. As a matter of fact, they define some of the exact same methods such as -tableNode:numberOfRowsInSection:
. The two sets of protocols don’t match up perfectly because ASTableNode
behaves a bit differently than UITableView
.
Find -wireDelegation
and replace tableView
with tableNode
in the implementation:
- (void)wireDelegation {
self.tableNode.dataSource = self;
self.tableNode.delegate = self;
}
Now, you’ll be told that AnimalTableController
doesn’t actually conform to the correct protocol. Currently, AnimalTableController
conforms to to UITableViewDataSource
and UITableViewDelegate
. In the following sections you will conform to and implement each of these protocols so that the view controller’s table node can function.
Conforming to ASTableDataSource
Towards the top of AnimalTableController.m, find the following DataSource
category interface declaration:
@interface AnimalTableController (DataSource)<UITableViewDataSource>
@end
and replace UITableViewDataSource
with ASTableDataSource
:
@interface AnimalTableController (DataSource)<ASTableDataSource>
@end
Now that AnimalTableController
declares conformance to ASTableDataSource
, it’s time to make it so.
Navigate toward the bottom of AnimalTableController.m and find the implementation of the DataSource
category.
First, change the UITableViewDataSource
method -tableView:numberOfRowsInSection:
to the ASTableDataSource
version by replacing it with the following.
- (NSInteger)tableNode:(ASTableNode *)tableNode numberOfRowsInSection:(NSInteger)section {
return self.animals.count;
}
Next, ASTableNode
s expect their cells to be returned in a different way than a UITableView
would. To accommodate the new paradigm replace -tableView:cellForRowAtIndexPath:
with the following method:
//1
- (ASCellNodeBlock)tableNode:(ASTableView *)tableView nodeBlockForRowAtIndexPath:(NSIndexPath *)indexPath {
//2
RainforestCardInfo *animal = self.animals[indexPath.row];
//3
return ^{
//4
CardNode *cardNode = [[CardNode alloc] initWithAnimal:animal];
//You'll add something extra here later...
return cardNode;
};
}
Let’s review this section by section:
-
An
ASCellNode
is the ASDK equivalent to aUITableViewCell
or aUICollectionViewCell
. The more important thing to notice is that this method returns anASCellNodeBlock
. This is because anASTableNode
maintains all of its cells internally and by giving it a block for each index path, it can concurrently initialize all of its cells when it’s ready. -
The first thing you do is grab a reference to the data model needed to populate this cell. This is a very important pattern to take note of. You grab the data and then capture it inside the following block. The
indexPath
shouldn’t be used inside the block, in case the data changes before the block is run. -
You then return a block whose return value must be an
ASCellNode
. -
There is no need to worry about cell reuse so just chill and initialize a cell the easy way. You may notice that you’re returning a
CardNode
now instead of aCardCell
.
This brings me to an important point. As you may have gathered, there is no cell reuse when using ASDK. Alright, maybe I already basically said that twice, but it’s a good thing to keep in mind. Feel free to go to the top of the class and delete
static NSString *kCellReuseIdentifier = @"CellReuseIdentifier";
You won’t be needing it anymore.
Maybe take a second to mull that over. You never have to worry about -prepareForReuse
again…
Conforming to ASTableDelegate
Towards the top of AnimalTableController.m, find the following Delegate
category interface declaration:
@interface AnimalTableController (Delegate)<UITableViewDelegate>
@end
and replace UITableViewDelegate
with ASTableDelegate
:
@interface AnimalTableController (Delegate)<ASTableDelegate>
@end
Now that AnimalTableController
declares conformance to ASTableDelegate
, it’s time to handle the implementation. Navigate towards the bottom of AnimalTableController.m and find the implementation of this Delegate
category.
As I’m sure you’re aware, with a UITableView you usually need to, at least, provide an implementation of -tableView:heightForRowAtIndexPath:
. This is because, with UIKit
, the height of each cell is calculated and returned by the table’s delegate.
ASTableDelegate
lacks -tableView:heightForRowAtIndexPath:
. In ASDK, all ASCellNode
s are responsible for determining their own size. Instead of being providing a static height, you can optionally define a minimum and maximum size for your cells. In this case, you want each cell to at least be as tall as 2/3rds of the screen.
Don’t worry about this too much right now; it’s covered in detail in part two of this series.
For now, just replace -tableView:heightForRowAtIndexPath:
with:
- (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);
}
After all your hard work, go ahead and build and run to see what you have.
That is one smooth table! Once you’ve composed yourself a little, get ready to make it even better.
Infinite Scrolling with Batch Fetching
In most apps, the server has more data points available than the number of cells you’d want to show in your average table. This means that darn near every app you work on will have some mechanism set up to load another batch of objects from the server as the user approaches the end of the current data set.
Many times, this is handled by manually observing the content offset in the scroll view delegate method -scrollViewDidScroll:
. With ASDK, there is a more declarative way of doing things. Instead, you can describe how many pages in advance you’d like to load new content.
The first thing you’ll do, is uncomment the helper methods that have been included. Go to the end of AnimalTableController.m and uncomment the two methods in the Helpers
category. You can think of -retrieveNextPageWithCompletion:
as your networking call, while -insertNewRowsInTableNode:
is a pretty standard method for adding new elements to a table.
Next, add the following line to -viewDidLoad:
.
self.tableNode.view.leadingScreensForBatching = 1.0; // overriding default of 2.0
Setting leadingScreensForBatching
to 1.0
means that you want new batches to be fetched whenever the user has scrolled to the point where only 1 screenful of content is left in the table before they would reach the end.
Next, add the following method to the Delegate
category implementation:
- (BOOL)shouldBatchFetchForTableNode:(ASTableNode *)tableNode {
return YES;
}
This method is used to tell the table whether or not it should keep making requests for new batches after this one. If you know you’ve reached the end of your API’s data, return NO
and no more requests will be made.
Since you really do want this table to scroll forever, just return YES to ensure new batches will always be requested.
Next, also add:
- (void)tableNode:(ASTableNode *)tableNode willBeginBatchFetchWithContext:(ASBatchContext *)context {
//1
[self retrieveNextPageWithCompletion:^(NSArray *animals) {
//2
[self insertNewRowsInTableNode:animals];
//3
[context completeBatchFetching:YES];
}];
}
This method is called when the user has neared the end of the table and the table has received a YES
from -shouldBatchFetchForTableNode:
.
Let’s review this section by section:
- First, you make a request for the next batch of animals to show. Usually this is an array of objects coming back from an API.
- On completion, update the table with the newly downloaded data.
- Finally, make sure to call
-completeBatchFetching:
withYES
when you’re done. New batch fetching requests won’t be made until this one has been completed.
Build, run, and just start swiping. Don’t stop until you don’t care to see another bird. They are infinite.