Skip to content

Simple, modular and easy to use variation of iOS's UITableViewController

Notifications You must be signed in to change notification settings

sidbatra/coffee-table-view-controller

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

39 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

CoffeeTableViewController

UITableViewController’s current architecture leads to bloated and disorganized table view controllers because it becomes a hub for different types of code, including:

  • View controller logic
  • Initializing table view cells
  • Presenting table view cells
  • Downloading images
  • Fetching or loading data to populate table view cells

This added complexity makes it hard to manage table view controllers with:

  • Multiple different types of cells
  • Multiple data sources, possibly from different services
  • Intricate cells that vary within a table view (Feed cells)
  • Cells that vary across related table views (Users who like an item, users to follow, users in search results)

CoffeeTableViewController is a modular, scalable and opinionated extension of UITableViewController.

Concepts

CoffeeTableViewController separates UITableViewController code into organized modules.

The data source contains an array of objects each of which is displayed as a table view row via a designated presenter. The table view controller defines this mapping between an object in the data source and a presenter.

Data Source

The data source contains code to fetch & populate the array of objects which represent the rows of the table view. Advanced table views with multiple sections are enabled via a two dimensional array. The data source communicates with the table view controller via a delegate.

Your custom data sources must inherit from CTableViewDataSource.

Presenter

Classes that implement the presenter protocol contain code to initialize, present, update and handle interactions with table view cells. Presenters have an associaed style attribute which enables different presentations of the same table view cell by simply changing a parameter.

Your custom model presenter classes must implement CModelPresenterProtocol.

Table View Controller

The table view controller contains an instance of the data source, view controller logic and a hash that maps data source objects to presenters.

Your custom table view controllers must inherit from CTableViewController.

Example

This section highlights the concepts discussed above via the Basic project found in the Examples folder. The aim is to render this simple table view of users:

UsersViewDataSource

The UsersViewDataSource inherits from CTableViewDataSource and it’s sole aim is to populate an array of objects that represents rows in the table view.

The [self addObjects:users] call populates this array of objects and [self.delegate reloadTableView] asks the table view to re-render.

- (void)loadUsers {
 NSMutableArray *users = [NSMutableArray arrayWithCapacity:10];

 //Populate users array - remotely or locally

 [self addObjects:users];   

 [self.delegate reloadTableView];
}

Most CTableViewDataSource methods contain a sectionIndex parameter that enables support for table views with multiple sections.

UserPresenter

The UserPresenter class implements CModelPresenterProtocol and wraps around the code to use UserCell for displaying users populated via UsersViewDataSource. The primary functions of the UserPresenter are:

Initialization and Presentation of UserCell

The presenation style parameter is defined by the UsersViewController along with mapping of User objects to UserCell views.

+ (UITableViewCell*)cellForObject:(id)object
                     withBaseCell:(id)base
               withCellIdentifier:(NSString*)identifier
                     withDelegate:(id)delegate
             andPresentationStyle:(NSInteger)style {
    
    User *user = object;
    UserCell *cell = base;
      
    //Initialization
    if(!cell)
        cell = [[UserCell alloc] initWithStyle:UITableViewStylePlain 
                                 reuseIdentifier:identifier];
    
    // Presentation
    [user downloadImage];
    [cell setUserImage:user.image];
    
    if(style == kUserPresenterStyleWithByline)
        [cell setUserName:user.name
               andMessage:user.byline];
    else
        [cell setUserName:user.name];
    
    return cell;
}

Update Presentation of UserCell

The UsersViewController is responsible for view controller logic and can update the presentation of visible cells by passing them an updated object. For example, if a User image is downloaded the UsersViewController can update visible UserCells by:

  [self provideResourceToVisibleCells:user
                           updatedKey:@"image"];

The CTableViewController bundles the updated object with each of the visible cells and calls:

+ (void)updatePresentationForCell:(id)base
                         ofObject:(id)object
            withPresentationStyle:(NSInteger)style
                withUpdatedObject:(id)updatedObject
                    andUpdatedKey:(NSString*)updatedKey {
    
    User *user = object;
    UserCell *cell = base;
    
    if(user == updatedObject) {
        if([updatedKey isEqualToString:@"image"])
            [cell setUserImage:user.image];
    }
}

This facilitates a table view UI that responds immediately to changes in not only the object it represents but to any attribute of any object that the table view controller feels is relevant.

Interactons with UserCell

The UsersViewController is automatically assigned as the delegate for all UserCell interactions. The presenter acts as a facade to package and fire delegate events.

+ (void)cellClickedForObject:(id)object
                withBaseCell:(id)base
       withPresentationStyle:(NSInteger)style
                withDelegate:(id)delegate {
    
    SEL sel = @selector(userCellSelected:);
    
    if(![delegate respondsToSelector:sel])
        return;
    
    User *user = object;
    
    [delegate performSelector:sel
                   withObject:user];
}

UsersViewController

The UsersViewController inherits from CTableViewController and primarily contains view logic. The primary functions of the UsersViewController are:

Setting up UsersViewDataSource

self.tableViewDataSource = [[UsersViewDataSource alloc] init];
[(UsersViewDataSource*)self.tableViewDataSource loadUsers];

Mapping Objects to Presenters

[self addModelPresenterForClass:[User class]
                      withStyle:kUserPresenterStyleWithByline 
                  withPresenter:[UserPresenter class]];

Displaying the UserCell without a byline is as simple as replacing kUserPresenterStyleWithByline with kModelPresenterDefaultStyle

Handling UserCell Events

- (void)userCellSelected:(User*)user {
    NSLog(@"User clicked: %@",user.name);
}

Updating Presentation of Visible Cells

- (void)userImageLoaded:(NSNotification*)notification {
    [self provideResourceToVisibleCells:notification.object
                             updatedKey:@"image"];
}

Extra

The Examples folder contains the Intermediate project which shows how CoffeeTableViewController works with more complex table views. This is the table view it renders.

Setup

Start by obtaining the source code, either by cloning the git repository:

git clone https://github.com/sidbatra/coffee-table-view-controller.git

or by downloading the latest zip or tar archive.

Add CoffeeTableViewController to your Xcode project:

  1. Drag and drop the CoffeeTableViewController folder into your project.
  2. Check the “Copy items into destination Group’s folder” and select Recursively create groups for any added folders.

and you’re done.

Customize

CTableViewController comes with loading, error and pull to refresh views. These views plug into the table view controller and can easily be replaced with your own views.

The CustomizedSupportingViews project in the Examples folder shows how to create a custom loading view. Create a custom view that implements the CLoadingViewProtocol and place the following method in your table view controller.

- (UIView*)tableLoadingView {
    CGRect frame = self.view.frame;
    return [[CustomLoadingView alloc] initWithFrame:CGRectMake(0,0,frame.size.width,frame.size.height)];
}

That’s it. The error and pull to refresh views can similarly be customized by views that implement their specific protocols.

Credit

CoffeeTableViewController is built by the wonderful people who make Mine: http://getmine.com

About

Simple, modular and easy to use variation of iOS's UITableViewController

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published