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
ASTableNode
to 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 UIView
s 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, 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.