This table.h shows how to create a tableView based iPhone app easily. This article as well as all the source code is public domain. However, please add my credit: www.superarts.org and you'll be free to go. Thanks. I'll use an example to explain how to use this header file. Suppose you want to create an app with 2 tabs, one ungrouped with contents "Visit Google", "Visit Yahoo", "Visit Super Art Studio", and one grouped, with sections "Help" (contents: "About Super Art Studio" and "About Super Art Software") and "Gallery" (contents: "Terran", "Zerg", and "Protoss"). Let's use some indent and it goes like: Table 1 (Ungrouped) Visit Google Visit Yahoo Visit Super Art Studio Table 2 (Grouped) Help About Super Art Studio About Super Art Software Gallery Terran Zerg Protoss And then we can start coding right now. 1. Preparation Use Interface Builder to create 2 tableViews, naming them table1 and table2, setting delegate and dataSource, etc. Put [self init_data]; in your didFinishLauching method. Add NSMutableArray* into class: data1, data2, data_table. You can name data1 and data2 to whatever you like. 2. Initialization Search the table.h with "table init example", and copy (yea, the ugly copy-and-paste) the #if...endif section to your code. Modify the strings and make it looks like: - (void)init_data { data1 = [[NSMutableArray alloc] initWithObjects: [[NSMutableArray alloc] initWithObjects: @"", nil], [[NSMutableArray alloc] initWithObjects: [[NSMutableArray alloc] initWithObjects: [[NSMutableArray alloc] initWithObjects: @"Visit Google", @"Visit Yahoo", @"Visit Super Art Studio", nil], nil], nil], nil]; data2 = [[NSMutableArray alloc] initWithObjects: [[NSMutableArray alloc] initWithObjects: @"Help", @"Gallery", nil], [[NSMutableArray alloc] initWithObjects: [[NSMutableArray alloc] initWithObjects: [[NSMutableArray alloc] initWithObjects: @"About Super Art Studio", @"About Super Art Software", nil], [[NSMutableArray alloc] initWithObjects: @"Terran", @"Protoss", @"Zerg", nil], nil], nil], nil]; data_table = [[NSMutableArray alloc] initWithObjects: [[NSMutableArray alloc] initWithObjects: table1, table2 nil], [[NSMutableArray alloc] initWithObjects: data1, data2, nil], nil]; } Quite straight-forward, huh? BTW, you can ignore the code contains "all" and "standard" for now. They are written for providing the 'different version' feature but this feature is just "not cool". I put them here in case someone wanna improve it. 3. Setting Delegate Simply copy-and-paste the 'table delegate example' #if...endif paragraph to your app. You can modify them if you want to change the appearance of the tables, but in this example let's assume you don't need customized appearance, so NOTHING NEEDS TO BE DONE. 4. Setting Handler Of course we want our app do something. Find the handle_select_cell method and modify it this way (pseudo-code inside): - (void)handle_select_cell:(NSString*)handle table:(UITableView*)table path:(NSIndexPath*)path { // NSLog(@"table select handler"); if (handle == @"Visit Google") go_url(@"www.google.com"); else if (handle == @"Visit Yahoo") go_url(@"www.yahoo.com"); else if (handle == @"Visit Super Art Studio") go_url(@"www.superarts.org"); else if (handle == @"About Super Art Studio") show_alert_view(@"Super Art Studio is awesome!"); else if (handle == @"About Super Art Software") show_alert_view(@"Super Art Software is cool!"); else push_gallery_view_with_picture(handle); } Hooray! Implement functions like go_url, show_alert_view etc. and you get a fully functioned iPhone app! ---- Full Code ---- #ifndef __TABLE_H #define __TABLE_H #define _g(x) NSLocalizedString(x, nil) NSInteger table_get_section_count(NSMutableArray* table, UITableView* view); NSString* table_get_section_title(NSMutableArray* table, UITableView* view, NSInteger section); NSInteger table_get_row_count(NSMutableArray* table, UITableView* view, NSInteger section); NSString* table_get_row_title(NSMutableArray* table, UITableView* view, NSIndexPath* path); NSString* table_get_handle(NSMutableArray* table, UITableView* view, NSIndexPath* path); #if 0 // table init example [self init_data]; - (void)init_data { dataX = [[NSMutableArray alloc] initWithObjects: [[NSMutableArray alloc] initWithObjects: @"section1", nil], [[NSMutableArray alloc] initWithObjects: [[NSMutableArray alloc] initWithObjects: [[NSMutableArray alloc] initWithObjects: @"row1", @"row2", nil], [[NSMutableArray alloc] initWithObjects: @"all", @"standard", nil], nil], nil], nil]; data_table = [[NSMutableArray alloc] initWithObjects: [[NSMutableArray alloc] initWithObjects: tableX, nil], [[NSMutableArray alloc] initWithObjects: dataX, nil], nil]; } #endif // table init example /* implementation */ #define TABLE_TITLE 0 #define TABLE_VERSION 1 #define TABLE_SECTION 0 #define TABLE_ROW 1 #define TABLE_VIEW 0 #define TABLE_DATA 1 NSInteger _table_get_section_count(NSMutableArray* table); NSString* _table_get_section_title(NSMutableArray* table, NSInteger section); NSInteger _table_get_row_count(NSMutableArray* table, NSInteger section); NSString* _table_get_row_title(NSMutableArray* table, NSIndexPath* path); NSString* _table_get_handle(NSMutableArray* table, NSIndexPath* path); NSInteger _table_get_section_count(NSMutableArray* table) { // NSLog(@"get section count"); return [[table objectAtIndex: TABLE_SECTION] count]; } NSString* _table_get_section_title(NSMutableArray* table, NSInteger section) { // NSLog(@"get section title"); return _g([[table objectAtIndex: TABLE_SECTION] objectAtIndex:section]); } NSInteger _table_get_row_count(NSMutableArray* table, NSInteger section) { // NSLog(@"get row count"); return [[[[table objectAtIndex: TABLE_ROW] objectAtIndex:section] objectAtIndex:TABLE_TITLE] count]; } NSString* _table_get_row_title(NSMutableArray* table, NSIndexPath* path) { // NSLog(@"get row title"); return _g([[[[table objectAtIndex: TABLE_ROW] objectAtIndex:path.section] objectAtIndex:TABLE_TITLE] objectAtIndex:path.row]); } NSString* _table_get_handle(NSMutableArray* table, NSIndexPath* path) { // NSLog(@"get handle"); return [[[[table objectAtIndex: TABLE_ROW] objectAtIndex:path.section] objectAtIndex:TABLE_TITLE] objectAtIndex:path.row]; } NSString* _table_get_version(NSMutableArray* table, NSIndexPath* path) { // NSLog(@"get version"); NSMutableArray* array = [[table objectAtIndex: TABLE_ROW] objectAtIndex:path.section]; if (array.count <= TABLE_VERSION) return @"all"; else { // NSLog(@"get version: %@", [[array objectAtIndex:TABLE_VERSION] objectAtIndex:path.row]); return [[array objectAtIndex:TABLE_VERSION] objectAtIndex:path.row]; } } NSString* _get_current_version(void) { #ifdef VERSION_LITE return @"lite"; #endif #ifdef VERSION_STANDARD return @"standard"; #endif // should not get here return @"all"; } /* determine if current version is in the 'ver' string */ BOOL is_version(NSString* ver) { if (ver == @"all") return YES; NSRange range = [ver rangeOfString:_get_current_version()]; // NSLog(@"is version: %@ vs %@ - %i vs %i", ver, _get_current_version(), range.length, NSNotFound); if ((range.location == NSNotFound) && (range.length == 0)) return NO; else return YES; return YES; } NSMutableArray* _table_get_data(NSMutableArray* table, UITableView* view) { int i; int count = [[table objectAtIndex:TABLE_VIEW] count]; for (i = 0; i < count; i++) { // NSLog(@"comparing %i, %i", [[table objectAtIndex:TABLE_VIEW] objectAtIndex:i], view); if ([[table objectAtIndex:TABLE_VIEW] objectAtIndex:i] == view) { // NSLog(@"table %@", table.description); return [[table objectAtIndex:TABLE_DATA] objectAtIndex:i]; } } NSLog(@"warning: table data not found: %i", count); return nil; } NSInteger table_get_section_count(NSMutableArray* table, UITableView* view) { NSMutableArray* data = _table_get_data(table, view); if (data == nil) return 1; else return _table_get_section_count(data); } NSString* table_get_section_title(NSMutableArray* table, UITableView* view, NSInteger section) { NSMutableArray* data = _table_get_data(table, view); if (data == nil) return @"unknown section title"; else return _table_get_section_title(data, section); } NSInteger table_get_row_count(NSMutableArray* table, UITableView* view, NSInteger section) { NSMutableArray* data = _table_get_data(table, view); if (data == nil) return 0; else return _table_get_row_count(data, section); } NSString* table_get_row_title(NSMutableArray* table, UITableView* view, NSIndexPath* path) { NSMutableArray* data = _table_get_data(table, view); NSString* s; if (data == nil) return @"unknown row title"; else s = _table_get_row_title(data, path); if (is_version(_table_get_version(data, path)) == NO) { // NSLog(@"get row title na: %@", s); s = [s stringByAppendingString:@" (N/A)"]; } return s; } NSString* table_get_handle(NSMutableArray* table, UITableView* view, NSIndexPath* path) { NSMutableArray* data = _table_get_data(table, view); if (is_version(_table_get_version(data, path)) == NO) return @"unknown handle"; if (data == nil) return @"unknown handle"; else return _table_get_handle(data, path); } #if 0 // table delegate example // tag set table section count - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return table_get_section_count(data_table, tableView); } // tag set table row count - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return table_get_row_count(data_table, tableView, section); } // tag set table header - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section { return table_get_section_title(data_table, tableView, section); } // tag set table cell - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *MyIdentifier = @"id table"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier]; if (cell == nil) { cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:MyIdentifier] autorelease]; } cell.text = table_get_row_title(data_table, tableView, indexPath); return cell; } // tag event table select - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { [self handle_select_cell:table_get_handle(data_table, tableView, indexPath) table:tableView path:indexPath]; } // Override if you support editing the list - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath { if (editingStyle == UITableViewCellEditingStyleDelete) { // Delete the row from the data source } if (editingStyle == UITableViewCellEditingStyleInsert) { // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view } } // Override if you support conditional editing of the list - (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath { // Return NO if you do not want the specified item to be editable. return NO; } // Override if you support rearranging the list - (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath { } // Override if you support conditional rearranging of the list - (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath { // Return NO if you do not want the item to be re-orderable. return NO; } - (UITableViewCellAccessoryType)tableView:(UITableView *)tableView accessoryTypeForRowWithIndexPath:(NSIndexPath *)indexPath { return UITableViewCellAccessoryNone; // return UITableViewCellAccessoryDetailDisclosureButton; // return UITableViewCellAccessoryCheckmark; } - (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath { NSLog(@"accessory button tapped"); } - (void)handle_select_cell:(NSString*)handle table:(UITableView*)table path:(NSIndexPath*)path { NSLog(@"table select handler"); } #endif // table delegate example #endif // __TABLE_H |
http://www.cocoachina.com/bbs/htm_data/6/0811/1067.html
Looks good. I have a few things that might help.
== for strings is not suggested, in case a string is a copy of another string. You want to compare value, not identity (memory location). In that case,
[string1 isEqual: string2] is suggested. If you know they're strings, then [string1 isEqualToString: string2] is faster.
When dealing with Obj-C, use #import instead of #include. #import already takes care of making sure something's not included twice, so you don't have to do #ifndef #define #endif structures.
If you intend to have this table view be a subview, then the data it stores will have to be released in dealloc. In the code, you use [alloc[init]], which needs a matching release. You could do [[[NSMutableArray alloc] initWithObjects: @"Terran", @"Protoss", @"Zerg", nil] autorelease] (Autorelease returns self, so that you can slip it in like that) but there's a better way.
Speaking of which, if you don't intend to edit or change the array once it's made, use NSArray. It's faster and has less overhead since it doesn't have to deal with the chance of ever being edited.
Ideally, what you want is to have is a property list (plist) that has all this information, and then to load it when needed. To make a plist, start to add a new file like you normally do in Xcode, but in the Mac OS X section, the "other" group, "Property List" is the option you want. It should go in the resource group. Let's call it table.plist for fun.
NSBundle * theBundle = [NSBundle mainBundle];
NSString * plistPath = [mainBundle pathForResource:@"table" ofType: @"plist"];
data_table = [[NSArray alloc] initWithContentsOfFile: plistPath];
This gives you one very cool bonus, besides replacing 40+ lines with 3. If you go to table.plist, and set it up for localization, in English, French, etc, NSBundle does the right thing. That is, it will automatically use the table's preferred language.
Localization of the list might have an impact on the comparisons elsewhere, so that'd be something to keep an eye on.
Still, the more iPhone resources, the better. Thanks for the web page!
- by Blain Hamon