Tableview

Modified

Disclosure

Example from The iPhone Developer's Cookbook by Erica Sadun, Addison Wesley ISBN-13 978-0-321-55545-8

Downloads

Search Table application

Video

Overview

UITableViewController provides a table view and navigation. Note that a UITableViewController does not hold the data but only presents the data.

The example displays colors in a scrollable table and provides user search on partial matches to produce a subset of the complete color list.

A table is typically filled from an array, in the example, a Model object manages this array. User input into the search field is passed to the Model which returns an array filled with partial matches.

The ViewController manages the view but also creates view programmatically. The view is primarily a table that displays the contents of the Model data. Table entries can also be selected by tapping, in this case the color of the navigation bar is changed to that of the selected row entry.

UITableViewController

Subclassing UITableViewController requires methods to be over-ridden:

-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView returns the number of sections in the table.

-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section returns the number of rows in section number; for the example, the size of the array filling the table.

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath returns the cell at the designated table row when only one section. Old cells are recycled when available, faster than creating new ones.

-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)newIndexPath called when user taps a row.

 
UISearchBar

Creating the search bar requires the following:

search = [[UISearchBar alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 280.0f, 44.0f)]; create an instance.

search.delegate = self; setting the delegate to receive notifications of changes to the search bar.

self.navigationItem.titleView = search; adding the search bar to the UITableViewController navigation.

Delegate methods for search bar called:

-(void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText on each search bar change. Each time a change received, the Model performs a partial match on data to construct a new array which is then displayed in the table.

-(void)searchBarSearchButtonClicked:(UISearchBar *)searchBar when the Done (Return) key of keyboard tapped, the keyboard is dismissed.

Color Table
#import <UIKit/UIKit.h>
#import "Model.h"

@interface ViewController : UITableViewController 
{
	NSMutableArray *searchArray;
	UISearchBar *search;
	Model *model;
}
- (UIColor *) getColor: (NSString *) hexColor;
@end

@implementation ViewController
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
	return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
	return [searchArray count];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
	NSInteger row = [indexPath row];
	
	// Create a cell if one is not already available
	UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:@"any-cell"];
	if (cell == nil) 
	    cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:@"any-cell"] autorelease];
	
	// Set up the cell by coloring its text
	NSArray *crayon = [[searchArray objectAtIndex:row] componentsSeparatedByString:@"#"];
	cell.text = [crayon objectAtIndex:0];
	cell.textColor = [self getColor:[crayon objectAtIndex:1]];
	return cell;
}

// Deselect the current table row selection
- (void) deselect {	
	[self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:YES];
}

// Respond to user selection tap by coloring the navigation bar
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)newIndexPath
{
	// Retrieve named color
	int row = [newIndexPath row];
	NSArray *crayon = [[searchArray objectAtIndex:row] componentsSeparatedByString:@"#"];
	
	// Update the nav bar color
	UIColor *newColor = [self getColor:[crayon objectAtIndex:1]];
	self.navigationController.navigationBar.tintColor = newColor;
	search.tintColor = newColor;
	
	// Deselect
	[self performSelector:@selector(deselect) withObject:NULL afterDelay:0.5];
}

// When the search text changes, update the array
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
{
	[model buildSearchArrayFrom: searchText];
	searchArray = [model searchArray];
	[self.tableView reloadData];
	if ([searchText length] == 0) [searchBar resignFirstResponder];
}

// When the search button (i.e. "Done") is clicked, hide the keyboard
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar
{
	[searchBar resignFirstResponder];
}

// Prepare the Table View
- (void)loadView {
	[super loadView];

	model = [Model new];
	[model buildSearchArrayFrom:@""];
	searchArray = [model searchArray];
	[self.tableView reloadData];
	
	search = [[UISearchBar alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 280.0f, 44.0f)];
	search.delegate = self;
	search.placeholder = @"color match text";
	search.autocorrectionType = UITextAutocorrectionTypeNo;
	search.autocapitalizationType = UITextAutocapitalizationTypeNone;
	self.navigationItem.titleView = search;
	[search release];

	// "Search" is the wrong key usage here. Replacing it with "Done"
	UITextField *searchField = [[search subviews] lastObject];
	[searchField setReturnKeyType: UIReturnKeyDone];
}

// Clean up
-(void) dealloc {
	[model release];
	[super dealloc];
}
// Convert a 6-character hex color to a UIColor object
- (UIColor *) getColor: (NSString *) hexColor
{
	unsigned int red, green, blue;
	NSRange range;
	range.length = 2;
	
	range.location = 0; 
	[[NSScanner scannerWithString:[hexColor substringWithRange:range]] scanHexInt:&red];
	range.location = 2; 
	[[NSScanner scannerWithString:[hexColor substringWithRange:range]] scanHexInt:&green];
	range.location = 4; 
	[[NSScanner scannerWithString:[hexColor substringWithRange:range]] scanHexInt:&blue];	
	
	return [UIColor colorWithRed:(float)(red/255.0f) 
			   green:(float)(green/255.0f) 
			   blue:(float)(blue/255.0f) alpha:1.0f];
}
@end

@interface AppDelegate : NSObject  
@end

@implementation AppDelegate
- (void)applicationDidFinishLaunching:(UIApplication *)application {	
	UIWindow *window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
	UINavigationController *nav = 
		[[UINavigationController alloc] initWithRootViewController:[[ViewController alloc] init]];
	[window addSubview: nav.view];
	[window makeKeyAndVisible];
}

- (void)dealloc {
	[super dealloc];
}
@end

int main(int argc, char *argv[])
{
	NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
	int retVal = UIApplicationMain(argc, argv, nil, @"AppDelegate");
	[pool release];
	return retVal;
}

Model

In the interest of completeness the Model class is provided.

There are several interesting points:

  1. Reading model data from a file named crayons.txt in the Other Sources. Here are a few entries:

    Almond #EED9C4
    Antique Brass #C88A65
    Apricot #FDD5B1
    Aquamarine #71D9E2

    and the code to read the file into colorArray:

    NSString *pathname = [[NSBundle mainBundle]  pathForResource:@"crayons" ofType:@"txt" inDirectory:@"/"];
    NSString *wordstring = [NSString stringWithContentsOfFile: pathname];
    colorArray = [[wordstring componentsSeparatedByString:@"\n"] retain];
     

  2. Performing partial matches on matchString with file entries to construct the array for table display; see -(void) buildSearchArrayFrom: (NSString *) matchString.
Model
#import <Foundation/Foundation.h>

@interface Model : NSObject
{
	NSMutableArray *colorArray;
	NSMutableArray *searchArray;
}
@property (readonly, nonatomic, retain) NSMutableArray *searchArray;
- (void) buildSearchArrayFrom: (NSString *) matchString;
@end

#import "Model.h"

@implementation Model
@synthesize searchArray;

// create an array by applying the search string
- (void) buildSearchArrayFrom: (NSString *) matchString
{
	NSString *upString = [[matchString uppercaseString] autorelease];
	if (searchArray) [searchArray release];
	
	searchArray = [[NSMutableArray alloc] init];
	for (NSString *word in colorArray)
	{
		if ([matchString length] == 0)
		{
			[searchArray addObject:word];
			continue;
		}
		
		NSRange range = [[[[word componentsSeparatedByString:@" #"] 
			                 objectAtIndex:0] uppercaseString] rangeOfString:upString];
		if (range.location != NSNotFound) [searchArray addObject:word];
	}
}

-(id) init {
	[super init];
	// Retrieve the text and colors from file
	NSString *pathname = [[NSBundle mainBundle]  pathForResource:@"crayons" 
               								        ofType:@"txt" 
									  inDirectory:@"/"];
	NSString *wordstring = [NSString stringWithContentsOfFile:pathname];
    	colorArray = [[wordstring componentsSeparatedByString:@"\n"] retain];
	[self buildSearchArrayFrom:@""];
	return self;
}

// Clean up
-(void) dealloc  {
	[colorArray release];
	[searchArray release];
	[super dealloc];
}
@end