Benjamin van der Veen

I live in Portland, Oregon. I work as a user experience designer at Emma.

Take a look at my portfolio, follow me on Twitter @bvanderveen, or email me at .

Sequence, UITableView

A great usecase for Sequence is to declaratively construct content for UITableView. As the user scrolls the table view, it pulls row data from a UITableViewDataSource for the row visible on the screen. In this post, I discuss how Sequence supports this usecase and demonstrate how to use Sequence for this purpose.

The seeking problem

Our implementation of itemAtIndex: supports short-circuit partial materialization of the sequence, but it doesn’t help us if we’re seeking to arbitary positions within the sequence. Suppose we had a table view with 1,000,000 rows of content represented by a sequence. If we call itemAtIndex: on that sequence to retrieve row information while we’re scrolling around the bottom of that list, we are calling itemAtIndex: with an argument of ~1,000,000 every few frames of animation. Our implementation of itemAtIndex: runs in O(n) time, so our table view animation grinds to a halt.

The best way to seek within a sequence is if you can jump to an easily-calculated offset. Arrays are great for this—you take a pointer to the start of the array, add the product of the desired index and the array element width, and you’ve calculated a pointer to the item at that index in O(1) time.

In order to support our table view scenario, we’ll make a time-space trade-off—we’ll copy the values of the sequence into a contiguous array, so we can easily seek to arbitrary positions within our array. We can do this by calling array on a sequence.

Table view

Let’s take a look at an abbreviated controller which specifies its contents in a declarative fashion. First, let’s introduce a construct which represents the content and behavior of a table row:

// an object which represents a single row of table content
@interface TableItem : NSObject {
    UITableViewCell *(^cell)();
    void (^action)();
}

// returns a cell to display the row
@property (nonatomic, copy) UITableViewCell *(^cell)(); 

// returns an action to be called when the row is selected
@property (nonatomic, copy) void (^action)(); 

@end

Now, a trivial table view implementation using that construct:

@implementation SequenceTableController

- (id)init {

    ...

        self.content = [[[[Seq rangeWithStart:1 end:500] // a sequence of numbers, 1..500
        filter: ^ BOOL (id i) { return [i intValue] % 2 == 0 }] // filter out the odd numbers
        map:^ id (id n) { // return a TableItem to represent the row
            NSString *s = [n description]; // turn the NSNumber into a string
            TableItem *item = [[TableItem new] autorelease];
            item.cell = [[^ id () { return [self cellWithLabelText:s]; } copy] autorelease];
            item.action = [[^ void () { [self alertText:s]; } copy] autorelease];
            return item;
        }] array]; // materialize the sequence into an array

    ...

}

...

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return content.length;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    TableItem *item = [content objectAtIndex:indexPath.row];
    return item.cell();
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    TableItem *item = [content objectAtIndex:indexPath.row];
    item.action();
}

@end

Not too bad. I’m imagining all the ways I could use this to avoid ever implementing UITableViewDelegate or UITableViewDataSource again.

Check out the full source code on GitHub.


Copyright © 2012 Benjamin van der Veen. atom feed