In this blog post, I am going to talk about implementing UITableViewController using open-source extension MwfTableViewController that I have implemented.

The posts will be published in multiple parts. Part 1 will cover the basic use case. Part 2 and more (have not been decided) will cover more advanced use cases.

Introduction

UITableViewController is probably one of the most commonly used controller in iOS apps. If you are an iOS developer, chances are you have already used it before.

I am not sure about anyone else, but I find out that it’s easy to repeat same if not similar codes in table view controller implementations. Many of them seems very much like boilerplates. But my concern is that it makes the controller class fat, ugly and very hard to maintain.

I want to put an end to this (or at least improve the situation), so I decided to write an extension to UITableViewController that would make implementing table view more pleasant and maintainable. The objectives are to reduce line-of-codes and improve organization (maintainability) of the codes.

As the time of writing this, MwfTableViewController is at version 0.0.4 and it’s being hosted at Github. If you are using Cocoapods, you can find the Specs here. The features can be summarized as followed:

  • Provide implementation of backing store for table data, at the same time provide better way to updates table content
  • Provide better design to create and configure table row cells
  • Provide improved support for search implementation
  • 100% unit-tested

Throughout this post, I’m going to use reference to Apple’s TableSearch sample codes, and provide the example codes as if it would have been written using MwfTableViewController.

Backing Store for Table Data

Let’s face it, using NSArray of object or NSArray of NSArray of object (table with sections) as table’s backing store for table data is not the best way to do it.

In my opinion, NSArray and UITableView’s datasource suffers some degrees of mismatch on the way it works. It’s most obvious when you need to perform batch updates to UITableView (insertion and deletion of rows). On NSArray, insertion or deletion of object affects the subsequent index of update operation. But when propagating the changes to UITableView, those indexes must be translated to NSIndexPath and/or NSIndexSet by adhering to rules. If you make mistake in translating them, your app would crash, not good!

If you are using NSArray as your backing store, you will always have to repeat this same codes to bridge those mismatch. So why not create backing store that work the same way UITableView datasource works so you can focus on your application logic instead.

That exactly what MwfTableData is for. You can either use this class on your UITableViewController implementation or as part of MwfTableViewController. The following will show example on the latter.

The API

MwfTableData API is grouped in 5 categories: creation, accessing data, inserting data, deleting data and updating data. Check MwfTableViewController.h file to find out about the methods.

Using with MwfTableViewController

Creating and providing initial data

Override method `createAndInitTableData`.

-(MwfTableData *)createAndInitTableData;
{
  MwfTableData * tableData = [MwfTableData createTableData];
  [tableData addRow:[Product productWithType:@"Device" name:@"iPhone"]];
  [tableData addRow:[Product productWithType:@"Device" name:@"iPod"]];
  [tableData addRow:[Product productWithType:@"Device" name:@"iPod touch"]];
  [tableData addRow:[Product productWithType:@"Desktop" name:@"iMac"]];
  [tableData addRow:[Product productWithType:@"Desktop" name:@"Mac Pro"]];
  [tableData addRow:[Product productWithType:@"Portable" name:@"iBook"]];
  [tableData addRow:[Product productWithType:@"Portable" name:@"MacBook"]];
  [tableData addRow:[Product productWithType:@"Portable" name:@"MacBook Pro"]];
  [tableData addRow:[Product productWithType:@"Portable" name:@"PowerBook"]];
  return tableData;
}

Reload Table Data

If your application needs to reload the table with new data, it can be done by setting `tableData` property (you don’t have to call [tableView reloadData]). Example:

-(void)reloadProducts;
{
  MwfTableData * tableData = [MwfTableData createTableData];
  // populate with reloaded data
  self.tableData = tableData; // will reload the table view
}

Batch Updates

A super convenience method `performUpdates:` is provided. Using this method, all the required updates to sync with tableView will be handled by MwfTableViewController. Example:

-(void)loadMoreProducts;
{
  NSArray * moreProducts = …; // load the products
  [self performUpdates:^(MwfTableData * tableData) {
    for (Product * p in moreProducts) {
      [tableData addRow:p];
    }
  }];
}

Before we move on to next section, it’s also worth mentioning that you do not need to implement `numberOfSectionsInTableView:` and `tableView:numberOfRowsInSection:` methods because they are already implemented for you.
Note: The implementation also takes care of scenario when you enable the search function.

Providing Table View Cell

Below shows the typical implementation to provide table view cell.

-(UITableViewCell*)tableView:(UITableView *)tv cellForRowAtIndexPath:(NSIndexPath *)ip;
{
  UITableViewCell * cell = nil;
  id item = [listOfItems objectAtIndex:ip.row];
  if ([item isKindOf:[Product class]]) {
    // create and configure cell for Product
  } else if ([item isKindOf:[ProductGroup class]]) {
    // create and configure cell for ProductGroup
  } // … and so on…
  return cell;
}

So, What’s the problem with the example above? Well, as I said earier it’s ugly, really ugly. What worse is it’s really hard to maintain.

To solve this, MwfTableViewController takes charge of implementing the `tableView:cellForRowAtIndexPath:` method.
What you need to do is implement the creation and configuration method for each of the data types (e.g. Product and ProductGroup) by following very simple naming convention.

Creation Method

`tableView:cellFor<DataClassName>AtIndexPath:`
or you can use short form `$cellFor(<DataClassName>)`

Configuration Method

`tableView:configCell:for<DataClassName>:`
or you can use short form `$configCell(<DataClassName>,<CellClassName>)`

To use the `$` short forms, you must define `CK_SHORTHAND` before importing `MwfTableViewController`.

#define CK_SHORTHAND
#import "MwfTableViewController.h"

Example:

-$cellFor(Product);
{
  static NSString * CellIdentifier = @"ProductCell";
  UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
  if (!cell) {
    cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
  }
  return cell;
}
-$configCell(Product,UITableViewCell);
{
  cell.textLabel.text = product.name;
}

That’s it, no more writing the endless and ugly if-else.
You can further improve your codes organization by implementing those methods in a objective-c category. By doing that, you keep your controller class lean and write the creation and configuration method for each data type ONCE.
Example:

@interface MwfTableViewController (TableSearchCells)
-$cellFor(Product);
-$configCell(Product,UITableViewCell);
-$cellFor(ProductGroup);
-$configCell(ProductGroup,ProductGroupCustomCell);
// … and so on
@end

Implementing Search

Using MwfTableViewController to implement search is dead simple. You basically just need to do 2 things:

Enable Search

By setting `wantSearch` property to YES.

-(id) initWithStyle:(UITableViewStyle)style;
{
  self = [super initWithStyle:style];
  if (self) {
    self.wantSearch = YES;
  }
  return self;
}

Override the create search results method

The method name is `createSearchResultsTableDataForSearchText:scope:`.

-(MwfTableData *)createSearchResultsTableDataForSearchText:(NSString *)searchText scope:(NSString *)scope;
{
  MwfTableData * resultData = [MwfTableData createTableData];
  NSUInteger count = self.tableData.numberOfRows;
  for (int i = 0; i < count; i++) {
    Product * product = [self.tableData objectForRowAtIndexPath:[NSIndexPath indexPathForRow:i inSection:0]];
    NSComparisonResult result = [product.name compare:searchText options:(NSCaseInsensitiveSearch|NSDiacriticInsensitiveSearch) range:NSMakeRange(0, [searchText length])];
    if (result == NSOrderedSame) {
      [resultData addRow:product];
    }
  }
  return resultData;
}

So that’s it. That’s all you need to do to implement basic table view with search function using MwfTableViewController.

If you would compare, it actually cuts around 50% of codes you need to write. Less codes means less bugs.
And you also get more elegant codes that is easier to maintain.

Finally, thanks for reading this post. Please share if you like it. If you have questions or feedbacks, you can post them in comments.

Advertisements

With little googling you will be able to find the instructions. I’m summarizing them below for convenience.

  • Choose your favorite theme, I use IR_Black

Upon installation, go to Preferences and make it your default theme.

  • To test it out, you can use this bash script to see how the colors look.

Save the script into .sh file and `chmod +x` to make it executable.

  • Change the emulation to `xterm-color`

Check out this post for the details.

  • Add alias ls=’ls -G’ in your ~/.bash_profile

With this, you enable colors for your files/folders when you execute ls command.

The first version of slide navigation controller was published 3 weeks ago. Few changes are made since then.

Deprecated Method

slideForViewController:direction:portraitOrientationDistance:landscapeOrientationDistance: method has been deprecated, although it’s still around to maintain the backward-compatibility.

The new version of the API require user to implement a new MWFSlideNavigationViewControllerDataSource protocol described below. To trigger the slide navigation, use the new method slideWithDirection:

New MWFSlideNavigationViewControllerDataSource protocol

This protocol has only 1 method to be implemented by application, i.e.

– (UIViewController *) slideNavigationViewController:(MWFSlideNavigationViewController *)controller viewControllerForSlideDirecton:(MWFSlideDirection)direction

Implementor should return instance of view controller to be shown for the specified slide direction.

Experimental Pan/Swipe Gesture

In this version, pan/swipe gesture is supported by setting panEnabled property to YES (default is NO).

If you have any feedback or suggestions for improvements, kindly leave them in comments.

Building an app with slide navigation to reveal a hidden view/menu beneath the main view, like one seen in Facebook and Piggie (picture of the left) ?

Well, that is exactly the purpose of the slide navigation view controller discussed in this post.

Why a view hidden beneath the main view?

I think there are at least 2 reasons for this design.

  • Reserve valuable screen real estate, this is true especially for the iPhone. With very limited space available, you want to keep only the important things front and center.
  • Cleaner/Uncluttered look and feel. For the sample apps mentioned above, It will easily clutter the screen when the ‘hidden’ view that contains menus/buttons are constantly shown.

MWFSlideNavigationViewController

This component is designed to be simple and easy to use. You call initWithRootViewController: method to initialize with a root UIViewController object. The root view controller is the main view of your application.

Subsequently, you can call slideForViewController:direction:portraitOrientationDistance:landscapeOrientationDistance: to reveal the secondary hidden view. The same method is used to slide the main view back.

This method takes 4 parameters:

  •  viewController, this is the view controller that manages the secondary hidden view. If you call this method to slide the main view back, you can supply nil value for the view controller.
  • direction, i.e. MWFSlideDirectionUp, MWFSlideDirectionLeft, MWFSlideDirectionDown, MWFSlideDirectionRight. To slide main view back, use MFWSlideDirectionNone.
  • portraitOrientationDistance, the slide distance in pixels when in portrait orientation.
  • landscapeOrientationDistance, the slide distance in pixels when in landscape orientation.

For custom/application specific behavior on the event when the sliding will/did occur, your can implement the MWFSlideNavigationViewControllerDelegate protocol. Example usage of the protocol is demonstrated in the Demo app, to show overlay view with tap gesture recognizer on the main view. When the overlay view is tapped, it slides the main view back to conceal the secondary view.

Additionally, there is a category included to provide convenient access to the slide navigation view controller to your view controller. Just like the built-in navigationController property in UIViewController.

How to use in your project

Download/fork the source code from Github, then add the MWFSlideNavigationController .h and .m files into your project.

Requirements

  • iOS 5 and above only
  • Full ARC

Source Codes and Documentation

The source codes are hosted at github.
You can refer to the documentation here.

Licensing

This is free and open source component covered under the MIT license. You are free to use and modify the source as long as you retain the copyrights and include permission notice in all copies/substantial portions of your software.

For attribution free license, you can contact me at meiwin@blockthirty.com.

Hope this component helps in your project. You can consider paypal donation to venansius.meiwin@gmail.com if you like what you see.

 

Updates: new post describing changes introduced in version 0.2

iOS development is a hot topic. It has been almost 4 years since Apple released the first iOS SDK available to developers to build native apps that run in iOS devices. Many books, tutorials, blogs, and websites have been published since then.

Recently I started to spend time working on apps again, after some period of idling.
Since then, iOS 5 had been released. And more useful resources online have been published and shared online, not to mention that a lot of them are free. These resources are blessings, they save time and energy.

And so I decided to put down the good stuffs I’ve found. I plan to update this post as I discover new one or make one that is worth sharing.

Websites/Posts/StackOverflow

List of websites and posts related to iOS development.

  • Ray Wenderlich, a website with abundant of iOS apps/games development tutorials. They also published a book, iOS 5 by Tutorials.
  • Cocoanetics, Objective-C blog with a lot of useful posts.
  • Storyboard tutorial, I had been the guy who used only codes for everything. But ever since I tried to use the new storyboard in iOS 5, I have changed my mind. The interface builder really saves a lot of time, I will not go back to the codes route unless necessary. I read this post to get started on storyboard.
  • CoreText, NSString is great, except you can not style it very much. But Apple has included CoreText since iOS 3.2. This post is great for you to get started.
  • Custom navigation bar, this post explains how to achieve custom styled navigation bar.
  • iOS fonts, this post lists the built-in fonts in iOS.

Components Marketplace

  • Cocoa Controls, a website that hosts iOS controls/components created by fellow developers. If you ever need a custom control, you should check here before creating one.
  • Binpress, this website sells components for many platforms including iOS. Most are paid components.
  • Chupamobile, focused on mobile platform only components.

From the Source

Controls/Libraries/Code Snippets

List of open-source components and code snippets

  • Delayed blocks, if you love blocks, you’ll love this.
  • Pull to Refresh, this mechanics was introduced and made popular by Tweetie app by atebits.
  • Styled page control, the built-in UIPageControl is very restricted. It doesn’t allow you to change its color. So if you need page control which colors suit your app, this one is for you.
  • Badge view, this control lets you use the number badge inside the app.
  • RichText Label, UILabel like control with support for styling using HTML-like markup.
  • iOSPlot, supports line and pie charts.
  • FlipTransform, flipboard like animation.
  • MKNetworkKit, full ARC based networking kit for iOS 4+ devices.
  • DTCoreText, library to allow creation of NSAttributedString from HTML code on iOS.
  • MWFSlideNavigationViewController, a container view controller that use slide animation to reveal hidden secondary view/menu beneath the main view. Read the blog post.
  • KNPathTableViewController. UITableViewController with customizable overlay panel while scrolling, inspired by Path.
  • iCarousel, a simple, highly customisable, data-driven 3D carousel for iOS and Mac OS.
  • JWFolders, Classes that attempt to imitate SpringBoard’s “folders”.
  • Logging, better way to log messages than using NSLog.

Graphic/Audio Assets

List of graphic/audio assets, such as design elements, icons, sounds, etc.

Wireframing

List of tools or graphics assets useful for creation apps wireframe.

Books

Books I have read.

Books I want to read.

Services

  • App localization, I have never used one before. If I do, I’ll post my experience here.

Ad Hoc OTA Distribution

  • Testflightapp, it provides OTA (over-the-air) distributions for adhoc builds. I have been using it for a while and it works great. So far it has been FREE.
  • Appblade, only heard about it recently therefore I have never tried it. If anyone has used it before, you may leave a comment about your experience.
%d bloggers like this: