Skip to content

TLIndexPathTools is a small set of classes that can greatly simplify your table and collection views.

License

Notifications You must be signed in to change notification settings

qiaoyan/TLIndexPathTools

Repository files navigation

TLIndexPathTools

TLIndexPathTools is a set of components designed to greatly simplify building rich, dynamic table and collection views. Here are some of the awesome things that TLIndexPathTools does:

  • Automatically calculate and perform animated inserts, deletes and moves.
  • Automatically organize the data model into sections.
  • Simplify implementing data source and delegate methods via rich data model APIs.
  • Provide a simpler alternative to Core Data's NSFetchedResultsController

##Installation

  1. Download the TLIndexPathTools project
  2. Add the TLIndexPathTools sub-folder (sibling of the Examples folder) to your Xcode project.
  3. Link to QuartzCore.framework and CoreData.framework (on the Build Phases tab of your project's target).

##Overview

Table and collection view batch updates are great. Users love apps that animate smoothly between states. But if you've done any non-trivial batch updates, you've probably found that they can make your view controller implementation very complex (and confusing). TLIndexPathTools makes all of this very easy by providing two simple classes TLIndexPathDataModel and TLIndexPathUpdates: TLIndexPathDataModel represents a single version of your data model, providing a rich API for organizing and accessing the data, and TLIndexPathUpdates takes two versions of your data model and performs the batch updates on your table or collection view.

Most of the functionality of TLIndexPathTools can be accomplished with just TLIndexPathDataModel and TLIndexPathUpdates. However, there are a number of additional components that build on these classes to make life a little bit easier.

  • TLIndexPathController provides a common programming model for building view controllers that work interchangeably with Core Data NSFetchRequests or plain arrays. One controller to rule them all.
  • TLTableViewController and TLCollectionViewController are base table and collection view implementations that provide the essential data source and delegate methods to get you up and running quickly with a few bells and whistles, like data-driven table cell height calculation, thrown in for good measure.
  • TLIndexPathItem is a wrapper class for your data items, useful for things like heterogenous data or multiple cell prototypes. Take a look at the Settings sample project, for example.
  • The Extensions folder contains a number of extensions for things like collapsable sections and expandable tree views. This is a good resource to see how TLIndexPathDataModel can be easily extended for special data structures.
  • And last, but not least, the Examples folder contains numerous sample projects demonstrating various use cases and features of the framework. Shuffle is a good starting point and be sure to try Core Data.

###TLIndexPathDataModel

TLIndexPathDataModel is an immutable object you use in your view controller to hold your data items instead of an array. There are three initializers, a basic one and two handling multiple sections:

// single section initializer
TLIndexPathDataModel dataModel1 = [TLIndexPathDataModel alloc] initWithItems:items];

// multiple sections defined by item key path
TLIndexPathDataModel dataModel2 = [TLIndexPathDataModel alloc] initWithItems:items sectionNameKeyPath:@"someKeyPath" identifierKeyPath:nil];

// multiple explicitly defined sections (including an empty section)
TLIndexPathSectionInfo section1 = [TLIndexPathSectionInfo alloc] initWithItems:@[@"Item 1.1"] name:@"Section 1"];
TLIndexPathSectionInfo section2 = [TLIndexPathSectionInfo alloc] initWithItems:@[@"Item 2.1", @"Item 2.2"] name:@"Section 2"];
TLIndexPathSectionInfo section3 = [TLIndexPathSectionInfo alloc] initWithItems:nil name:@"Section 3"];
TLIndexPathDataModel dataModel3 = [TLIndexPathDataModel alloc] initWithSectionInfos:@[section1, section2, section3] identifierKeyPath:nil];

And there are numerous APIs to simplify delegate and data source implementations:

// access all items across all sections as a flat array
dataModel.items;

// access items organized by sections
dataModel.sections;

// number of sections
[dataModel numberOfSections];

// number of rows in section
[dataModel numberOfRowsInSection:section];

// look up item at a given index path
[dataModel itemAtIndexPath:indexPath];

// look up index path for a given item
[dataModel indexPathForItem:item];

As an immutable object, all of the properties and methods in TLIndexPathDataModel are read-only. So using the data model is very straightforward once you've selected the appropriate initializer.

###TLIndexPathUpdates

TLIndexPathUpdates is where the real magic happens. You provide two versions of your data model to the initializer of TLIndexPathUpdates and the inserts, deletes, and moves are calculated right then and there. Then call either performBatchUpdatesOnTableView: or performBatchUpdatesOnCollectionView: and see the batch updates performed in all their animated glory.

// initialize collection view with unordered items
// (assuming view controller has a self.dataModel property)
self.dataModel = [TLIndexPathDataModel alloc] initWithItems:@[@"B", @"A", @"C"];
[self.collectionView reloadData];

// ...

// sort items, update data model & perform batch updates (perhaps when a sort button it tapped)
TLIndexPathDataModel *oldDataModel = self.dataModel;
NSArray *sortedItems = [self.dataModel.items sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];
self.dataModel = [TLIndexPathDataModel alloc] initWithItems:sortedItems];
TLIndexPathUpdates *updates = [TLIndexPathUpdates alloc] initWithOldDataModel:oldDataModel updatedDataModel:self.dataModel];
[updates performBatchUpdatesOnCollectionView:self.collectionView];

Thats all it takes!

###TLIndexPathController

TLIndexPathController is TLIndexPathTools' version of NSFetchedResultsController. It should not come as a surprise, then, that you must use this class if you want to integrate with Core Data.

Although it primarily exists for Core Data integration, TLIndexPathController works interchangeably with NSFetchRequest or plain arrays with any data type. Thus, if you choose to standardize your view controllers on TLIndexPathController, it is possible to have a common programming model across all of your table and collection views.

TLIndexPathController also makes a few nice improvements relative to NSFetchedResultsController:

  • Items do not need to be presorted by section. The data model handles organizing sections.
  • Changes to your fetch request are animated. So you can get animated sorting and filtering.
  • There is only one delegate method to implement (versus NSFetchedResultsController's five).

The basic template for using TLIndexPathController in a (table) view controller is as follows:

#import <UIKit/UIKit.h>
#import "TLIndexPathController.h"
@interface ViewController : UITableViewController <TLIndexPathControllerDelegate>
@end

#import "ViewController.h"
@interface ViewController ()
@property (strong, nonatomic) TLIndexPathController *indexPathController;
@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    self.indexPathController = [[TLIndexPathController alloc] init];
}

#pragma mark - UITableViewDataSource

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return self.indexPathController.dataModel.numberOfSections;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return [self.indexPathController.dataModel numberOfRowsInSection:section];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"];
    id item = [self.indexPathController.dataModel itemAtIndexPath:indexPath];
    //configure cell using data item
    return cell;
}

#pragma mark - TLIndexPathControllerDelegate

- (void)controller:(TLIndexPathController *)controller didUpdateDataModel:(TLIndexPathUpdates *)updates
{
    [updates performBatchUpdatesOnTableView:self.tableView withRowAnimation:UITableViewRowAnimationFade];    
}

@end

This template works with plain arrays or NSFetchRequests. With plain arrays, you simply set the dataModel property of the controller (or set the items property and get a default data model). With NSFetchRequests, you set the fetchRequest property and call performFetch:. From then on, the controller updates the data model interinally every time the fetch results change (using an internal instance of NSFetchedResultsController and responding to controllerDidChangeContent messages).

In either case, whether you explicitly set a data model or the controller converts a fetch result into a data model, the controller creates the TLIndexPathUpdates object for you and passes it to the delegate, giving you an opportunity to perform batch updates:

- (void)controller:(TLIndexPathController *)controller didUpdateDataModel:(TLIndexPathUpdates *)updates
{
    [updates performBatchUpdatesOnTableView:self.tableView withRowAnimation:UITableViewRowAnimationFade];    
}

###TLTableViewController & TLCollectionViewController

TLTableViewController and TLCollectionViewController are base table and collection view implementations, providing the essential data source and delegate methods to get you up and running quickly. Both classes look much like the code outlined above for integrating with TLIndexPathController with a few bells and whistles thrown in.

Most notably, TLTableViewController includes a default implementation of heightForRowAtIndexPath that calculates static or data-driven cell heights using prototype cell instances. For example, if you're using storyboards, the cell heights specified in the storyboard are automatically used. And if your cell implements the TLDynamicSizeView protocol, the height will be determined by calling the sizeWithData: method on the prototype cell. This is a great way to handle data-driven height because the sizeWithData: method can use the actual layout logic of the cell itself, rather than duplicating the layout logic in the view controller.

Most of the sample projects are based on TLTableViewController or TLCollectionViewController, so a brief perusal will give you a good idea what can be accomplished with a few lines of code.

About

TLIndexPathTools is a small set of classes that can greatly simplify your table and collection views.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • Objective-C 82.9%
  • HTML 12.1%
  • CSS 4.2%
  • Other 0.8%