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?
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.
Intelligent Preloading
Have you ever worked on an app where you decided to load content in advance in some kind of scroll view or page view controller? Maybe you were working on a full-screen image gallery and you decided you always wanted the next few images to be loaded and waiting so your users rarely saw a placeholder.
When you do work on a system like this, you soon realize there’s a lot to think about.
- How much memory are you taking up?
- How far in advance should you be loading content?
- When do you decide to dump what you have in response to user interaction?
And this gets quite a lot more complex when you factor in multiple dimensions of content. Do you have a page view controller with a collection view inside of each of the view controllers? Now you need to think of how you’re going to dynamically load content in both directions… Also, go ahead and tune that for each device you’re supporting. K, thanks.
Remember how I told you to to keep that ASRangeController
thing on the back burner of your mind? Well move it to the front burner!
Within each of the container classes there is a concept of the interface state for each of the contained nodes. At any given time, a node can be in any combination of:
- Preload Range: Usually the furthest range out from being visible. This is when content for each subnode in a cell, such as an ASNetworkImageNode, should be loaded from some external source; an API or a local cache for example. This is in contrast to batch fetching which should be used to fetch model objects representing cells themselves.
- Display Range: Here, display tasks such as text drawing and image decoding take place.
- Visible Range: At this point, the node is onscreen by at least one pixel.
These ranges also work on the metric of “screenfuls” and can be easily tuned using the ASRangeTuningParameters
property.
For example, you’re using an ASNetworkImageNode
to display the image in each page of the gallery. Each one will request data from the network when it enters the Preload Range and decode the image it has retrieved when it enters the Display Range.
In general, you don’t have to think too hard about these ranges if you don’t want to. The built in components, such as ASNetworkImageNode
and ASTextNode
, take full advantage of them which means you will see huge benefits by default.
In general, the leading side of the range is larger than the trailing side. When the user changes their scroll direction, the sizes of the ranges reverse as well in order to favor the content the user is actually moving toward.
Node Interface State Callbacks
You’re probably wondering how exactly these ranges work right? I’m glad you asked.
Every node in the system has an interfaceState
property which is a “bitfield” (NS_OPTION) type ASInterfaceState
. As the ASCellNode
moves through a scroll view managed by an ASRangeController
, each subnode has its interfaceState
property updated accordingly. This means that even the deepest nodes in the tree can respond to interfaceState
changes.
Luckily, it’s rarely necessary to fiddle with the bits of a node’s interfaceState
directly. More often, you’ll just want to react to a node changing to or from a certain state. That’s where the interface state callbacks come in.