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
You are currently viewing page 2 of 4 of this article. Click here to view the first page.

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:

  1. An ASCellNode is the ASDK equivalent to a UITableViewCell or a UICollectionViewCell. The more important thing to notice is that this method returns an ASCellNodeBlock. This is because an ASTableNode 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.
  2. 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.
  3. You then return a block whose return value must be an ASCellNode.
  4. 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 a CardCell.

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.

AfterTableNodeBeforePager

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:

  1. 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.
  2. On completion, update the table with the newly downloaded data.
  3. Finally, make sure to call -completeBatchFetching: with YES 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.

InfiniteScrollingGif