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?
Converting the TableView
The first thing you’ll do is to convert the current table view into a table node. Doing this is relatively straightforward.
Replacing tableView with tableNode
First, navigate to AnimalTableController.m. Add the following line below the other imports in this class:
#import <AsyncDisplayKit/AsyncDisplayKit.h>
This imports ASDK in order to use the framework.
Then, go ahead and replace the following property declaration of tableView:
@property (strong, nonatomic) UITableView *tableView;
with the following tableNode:
@property (strong, nonatomic) ASTableNode *tableNode;
This will cause a lot of code in this class to break, but do not panic!

Seriously, don’t worry. These errors and warnings will serve as your guide in the task of converting what you currently have into what you really want.
The errors in -viewDidLoad are, of course, to do with the fact that the tableView doesn’t exist anymore. I’m not going to make you go through and change all the instances of tableView to tableNode (I mean, find and replace isn’t that hard so feel free to) but if you did you’d see that:
-
You should be assigning an
ASTableNodeto the property. -
A table node doesn’t have a method called
-registerClass:forCellReuseIdentifier:. - You can’t add a node as a subview.
At this point you should just replace -viewDidLoad with the following:
- (void)viewDidLoad {
[super viewDidLoad];
[self.view addSubnode:self.tableNode];
[self applyStyle];
}
The interesting thing to note here is that you’re calling -addSubnode: on a UIView. This method has been added to all UIViews via a category, and is exactly equivalent to:
[self.view addSubview:self.tableNode.view];
Next, fix -viewWillLayoutSubviews by replacing that method definition with the following:
- (void)viewWillLayoutSubviews {
[super viewWillLayoutSubviews];
self.tableNode.frame = self.view.bounds;
}
All this does is replace self.tableView with self.tableNode to set the table’s frame.
Next, find the -applyStyle method and replace the implementation with the following:
- (void)applyStyle {
self.view.backgroundColor = [UIColor blackColor];
self.tableNode.view.separatorStyle = UITableViewCellSeparatorStyleNone;
}
The line that sets the table’s separatorStyle is the only line that changed. Notice how the table node’s view property is accessed in order to set the table’s separatorStyle. ASTableNode does not expose all the properties of UITableView, so you have to access the table node’s underlying UITableView instance in order to change UITableView specific properties.
Then, add the following line at the very beginning of -initWithAnimals:
_tableNode = [[ASTableNode alloc] initWithStyle:UITableViewStylePlain];
and add the following at the end, before the initializer’s return statement:
[self wireDelegation];
This initializes AnimalTableController with a table node and calls -wireDelegation to wire up the table node’s delegates.
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, ASTableNodes 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
ASCellNodeis the ASDK equivalent to aUITableViewCellor aUICollectionViewCell. The more important thing to notice is that this method returns anASCellNodeBlock. This is because anASTableNodemaintains 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
indexPathshouldn’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
CardNodenow 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 ASCellNodes 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.