Are you sick of all the UI Table View Controller examples on the internet where every delegate or dataSource method is littered with IF statements? Does adding one row or section require you to make a zillion changes across multiple methods? Well, I have a very simple alternative to that unmaintainable coding style.
Your current situation:
Your Table View Controller inherits from UITableViewController. In order to render all the sections and rows, you must override a series of methods on UITableViewDelegate and UITableViewDataSource protocols. If your app has many Table View Controllers, doing this over and over again gets old fast.
An easier solution:
The NoIfStatementUITableViewController implements most of the commonly used delegate and dataSource methods for you using its Model (in the MVC pattern) called a TableOptionList. In your inherited View Controller, you interact only with the TableOptionList by adding sections, buttons and footers, specifying a @selector method for each cell in your table view.
Step by Step Instructions:
Include the six attached files in your project.
TableOptionList.h
TableOptionList.m
TableOption.h
TableOption.m
NoIfStatementUITableViewController.h
NoIfStatementUITableViewController.m
Add a new class (e.g. OlympicCitiesViewController), inheriting from NoIfStatementUITableViewController.
Implement the default initializer, -(id)initWithStyle:(UITableViewStyle)style
Just like any class, call the super class initializer.
In the display order you want, add a header like so:
[self.tableOptionList addHeader:@"Future Olympics"];
Then, add a button with a @selector for each cell in that section:
[self.tableOptionList addButton:@"London 2012" action:@selector(londonDetails)];
[self.tableOptionList addButton:@"Sochi 2014" action:@selector(sochiDetails)];
[self.tableOptionList addButton:@"Rio de Janeiro 2016" action:@selector(rioDeJaneiroDetails)];
Optionally, add a footer to that section:
[self.tableOptionList addFooter:@"Cities 2018 and beyond TBA"];
Add other sections:
[self.tableOptionList addHeader:@"Past Olympics"];
Add buttons in the second section:
[self.tableOptionList addButton:@"Vancouver 2010" action:@selector(vancouverDetails)];
[self.tableOptionList addButton:@"Beijing 2008" action:@selector(beijingDetails)];
Implement the selectors as methods in OlympicCitiesViewController
-(void) londonDetails{
// show London info
}
You're done. No if statements in your code. When the contents of your list changes, all you need to do is add a button and a @selector method.
Your comments and feedback are always welcome.
If you decide to refactor or extend this code, the unit tests for TableOptionList are here:
TableOptionListsTests.h (using GHUnit):
#import <Foundation/Foundation.h> #import <GHUnitIOS/GHUnit.h> @interface TableOptionsListTests : GHTestCase { } @end
TableOptionListsTests.m (using GHUnit):
#import "TableOptionsListTests.h" #import "TableOptionList.h" #import "TableOption.h" @interface TableOptionsListTests() @property (retain) TableOptionList* tableOptionList; @property BOOL bogusSelectorWasCalled; @property BOOL askingForCheckmarkWasCalled; @property BOOL customizeCellWasCalled; @end @implementation TableOptionsListTests @synthesize tableOptionList; @synthesize bogusSelectorWasCalled; @synthesize askingForCheckmarkWasCalled; @synthesize customizeCellWasCalled; -(void)sampleSelectorMethod { self.bogusSelectorWasCalled = YES; } -(void)setUp { self.bogusSelectorWasCalled = NO; self.tableOptionList = [[TableOptionList alloc] init]; self.askingForCheckmarkWasCalled = NO; self.customizeCellWasCalled = NO; [self.tableOptionList addHeader:@"Header 1"]; [self.tableOptionList addButton:@"1a" action:@selector(bogus1) customizeCell:^(UITableViewCell *cell) { self.customizeCellWasCalled = YES; }]; [self.tableOptionList addButton:@"1b" action:@selector(bogus2) shouldHaveCheckmark:^BOOL() { self.askingForCheckmarkWasCalled = YES; return YES; }]; [self.tableOptionList addFooter:@"Footer 1"]; [self.tableOptionList addHeader:@"Header 2"]; [self.tableOptionList addButton:@"2a" action:@selector(sampleSelectorMethod)]; [self.tableOptionList addSection]; [self.tableOptionList addButton:@"3a" action:@selector(bogus4)]; } -(void)tearDown { [self.tableOptionList release]; } -(void)testHeaderTitleBySectionNumber { NSString* actualHeaderInFirstSection = [self.tableOptionList headerTitleInSection:0]; GHAssertEqualStrings(actualHeaderInFirstSection, @"Header 1", nil); NSString* actualHeaderInSecondSection = [self.tableOptionList headerTitleInSection:1]; GHAssertEqualStrings(actualHeaderInSecondSection, @"Header 2", nil); GHAssertNil([self.tableOptionList headerTitleInSection:2], nil); } -(void)testFooterTitleBySectionNumber { NSString* actualFooterInFirstSection = [self.tableOptionList footerTitleInSection:0]; GHAssertEqualStrings(actualFooterInFirstSection, @"Footer 1", nil); GHAssertNil([self.tableOptionList footerTitleInSection:1], nil); GHAssertNil([self.tableOptionList footerTitleInSection:2], nil); } -(void)testFindByIndexPathCanInvokeSelector { NSUInteger section = 1; NSUInteger row = 0; NSIndexPath* path = [NSIndexPath indexPathForRow:row inSection:section]; TableOption* option2 = [self.tableOptionList findBy:path]; [self performSelector:option2.action]; GHAssertTrue(self.bogusSelectorWasCalled, nil); GHAssertEqualStrings(option2.title, @"2a", nil); } -(void)testNumberOfSections { NSUInteger expectedSectionCount = 3; NSUInteger actualSectionCount = self.tableOptionList.numberOfSections; GHAssertEquals(expectedSectionCount, actualSectionCount, nil); } -(void)testRowCountInFirstSection { NSUInteger expectedRowsInFirstSectionCount = 2; NSUInteger actualRowsInFirstSection = [self.tableOptionList rowsInSection:0]; GHAssertEquals(actualRowsInFirstSection, expectedRowsInFirstSectionCount, nil); } -(void)testShouldHaveCheckmarkCallback { NSUInteger sectionFor1b = 0; NSUInteger rowFor1b = 1; NSIndexPath* pathFor1b = [NSIndexPath indexPathForRow:rowFor1b inSection:sectionFor1b]; TableOption* optionFor1b = [self.tableOptionList findBy:pathFor1b]; ShouldHaveCheckmark callback = [optionFor1b shouldHaveCheckmarkCallback]; GHAssertNotNil(callback, nil); BOOL shouldHaveCheckmarkFor1b = callback(); GHAssertTrue(shouldHaveCheckmarkFor1b, nil); GHAssertTrue(self.askingForCheckmarkWasCalled, nil); } -(void)testCustomizeCellCallback { NSUInteger sectionFor1a = 0; NSUInteger rowFor1a = 0; NSIndexPath* pathFor1a = [NSIndexPath indexPathForRow:rowFor1a inSection:sectionFor1a]; TableOption* optionFor1a = [self.tableOptionList findBy:pathFor1a]; CustomizeCell bar = [optionFor1a customizeCellCallback]; GHAssertNotNil(bar, nil); bar(nil); GHAssertTrue(self.customizeCellWasCalled, nil); } @end