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.

Leave a rating/review
Save for later
Share

“Art is anything you can do well. Anything you can do with Quality.”
—Robert M. Pirsig

AsyncDisplayKit is a UI framework that was originally born from Facebook’s Paper app. It came as an answer to one of the core questions the Paper team faced: how can you keep the main thread as clear as possible?

Nowadays, many apps have a user experience that relies heavily upon continuous gestures and physics based animations. At the very least, your UI is probably dependent on some form of scroll view.

These types of user interfaces depend entirely on the main thread and are extremely sensitive to main thread stalls. A clogged main thread means dropped frames and an unpleasant user experience.

Some of the big contributors to main thread work include:

  • Measurement and Layout: Things like -heightForRowAtIndexPath: or calling -sizeThatFits on a UILabel as well as the exponential cost of AutoLayout‘s constraint solver.
  • Image Decoding: Using a UIImage in an image view means the image data needs to be decoded first.
  • Drawing: Intricate text as well as manually drawing gradients and shadows.
  • Object Life Cycle: Creating, manipulating and destroying system objects (ie. creating a UIView).

When used correctly, AsyncDisplayKit allows you to perform all measurement, layout and rendering asynchronously by default. Without any extra optimization an app can experience roughly an order of magnitude reduction in the amount of work done on the main thread.


In addition to these performance wins, modern AsyncDisplayKit offers an impressive set of developer conveniences that allow implementing complex, sophisticated interfaces with a minimum of code.

In this two part AsyncDisplayKit 2.0 tutorial, you’ll learn all the essentials to build a useful and dynamic application with ASDK. In part one, you’ll learn some big picture ideas you can use when architecting an app. In part two, you’ll learn how to build your own node subclass as well as how to use ASDK’s powerful layout engine. In order to complete this tutorial you will need Xcode 7.3 and familiarity with Objective-C.

Disclaimer: ASDK is incompatible with both Interface Builder and AutoLayout, so you won’t be using them in this tutorial. Although ASDK fully supports Swift (a distinction from ComponentKit), many of its users are still writing Objective-C. At the moment, the majority of the top 100 free apps don’t include any Swift at all (at least 6 use ASDK). For these reasons, this series will focus on Objective-C. That being said, we’ve included a Swift version of the sample project in case you hate staples.

Getting Started

To begin, go ahead and download the starter project.

The project uses CocoaPods to pull in AsyncDisplayKit. So, in usual CocoaPods style, go ahead and open RainforestStarter.xcworkspace but NOT RainforestStarter.xcodeproj.

Note: A network connection is required to work through this tutorial.

Build and run to see an app consisting of one UITableView containing a list of animals. If you look at the code in AnimalTableController you’ll see that it’s a normal UITableViewController class you’ve probably seen plenty of times.

Note: Make sure to run the code in this tutorial on a physical device instead of in the simulator.

Scroll through the animals and notice the number of frames that are being dropped. You don’t need to fire up Instruments to be able to see that this app needs some help in the performance department.

LaggyScrolling

You can fix that, through the power of AsyncDisplayKit.

Introducing ASDisplayNode

ASDisplayNode is the core class of ASDK and is, at its heart, just an MVC “view” object in the same way as a UIView or CALayer. The best way to think about a node is by thinking about the relationship between UIViews and CALayers that you should already be familiar with.

Remember that everything onscreen in an iOS app is represented via a CALayer object. UIViews create and own a backing CALayer to which they add touch handling and other functionality. UIViews themselves are not a CALayer subclass. Instead, they wrap around a layer object, extending its functionality.

view-layer

This abstraction is extended in the case of ASDisplayNode: you can think of them as wrapping a view, just like a view wraps a layer.

What nodes bring to the table over a regular view is the fact that they can be created and configured on background queues and are concurrently rendered by default.

node-view-layer

Luckily, the API for dealing with nodes should be incredibly familiar to anyone who’s used UIViews or CALayers. All the view properties you would normally use are available on the equivalent node class. You can even access the underlying view or layer itself — just as you can access the .layer of a UIView.

The Node Containers

While nodes themselves provide the possibility of vast performance improvements, the real magic happens when they’re used in conjunction with one of the four container classes.

These classes include:

  • ASViewController: A UIViewController subclass that allows you to provide the node you want to be managed.
  • ASCollectionNode and ASTableNode: Node equivalents to UICollectionView and UITableView, a subclass of which is actually maintained under the hood.
  • ASPagerNode: A subclass of ASCollectionNode which offers great swiping performance compared to UIKit’s UIPageViewController.

ragecomic

Fair enough, but the real magic comes from the ASRangeController each of these classes uses to influence the behavior of the contained nodes. For now, just trust me and keep that in the back of your head for later.

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!

butBut

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:

  1. You should be assigning an ASTableNode to the property.
  2. A table node doesn’t have a method called -registerClass:forCellReuseIdentifier:.
  3. 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.