You’ve got a table view on the screen, but it has no clue about what it should display. As a view object, the table view does not contain anything about actual data. It needs an object to act as its data source. In iTahDoodle, your table view’s data source will be the instance of BNRAppDelegate.
In BNRAppDelegate.m, update application:didFinishLaunchingWithOptions: to send a message to the table view that makes the BNRAppDelegate instance its data source:
... // Create and configure the table view taskTable = [[UITableView alloc] initWithFrame:tableFrame style:UITableViewStylePlain]; [taskTable setSeparatorStyle:UITableViewCellSeparatorStyleNone]; // Make this object the table view's dataSource [taskTable setDataSource:self]; // Create and configure the text field where new tasks will be typed taskField = [[UITextField alloc] initWithFrame:fieldFrame]; ...
In order for the table view’s data source to do its job, it must implement methods in the UITableViewDataSource protocol. First, update BNRAppDelegate.h to declare that BNRAppDelegate conforms to this protocol:
@interface BNRAppDelegate : UIResponder <UIApplicationDelegate, UITableViewDataSource> { UITableView *taskTable; UITextField *taskField; UIButton *insertButton; NSMutableArray *tasks; } - (void)addTask:(id)sender;
The UITableViewDataSource protocol has two required methods that BNRAppDelegate must now implement. At a minimum, a table view’s data source must be prepared to tell the table view how many rows are in a given section of the table, and what the cell in a given row should be.
Implement the callbacks accordingly:
#pragma mark - Table View management - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { // Because this table view only has one section, // the number of rows in it is equal to the number // of items in our tasks array return [tasks count]; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { // To improve performance, we reconfigure cells in memory // that have scrolled off the screen and hand them back // with new contents instead of always creating new cells. // First, we check to see if there's a cell available for reuse. UITableViewCell *c = [taskTable dequeueReusableCellWithIdentifier:@"Cell"]; if (!c) { // ...and only allocate a new cell if none are available c = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"Cell"]; } // Then we (re)configure the cell based on the model object, // in this case our todoItems array NSString *item = [tasks objectAtIndex:[indexPath row]]; [[c textLabel] setText:item]; // and hand back to the table view the properly configured cell return c; }
To test the application, add some data directly to the array at the top of application:didFinishLaunchingWithOptions:.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // Attempt to load an existing to-do dataset from an array stored to disk. NSArray *plist = [NSArray arrayWithContentsOfFile:docPath()]; if (plist) { // If there was a dataset available, copy it into our instance variable. tasks = [plist mutableCopy]; } else { // Otherwise, just create an empty one to get us started. tasks = [[NSMutableArray alloc] init]; } // Is tasks empty? if ([tasks count] == 0) { // Put some strings in it [tasks addObject:@"Walk the dogs"]; [tasks addObject:@"Feed the hogs"]; [tasks addObject:@"Chop the logs"]; } // Create and configure the UIWindow instance CGRect windowFrame = [[UIScreen mainScreen] bounds]; UIWindow *theWindow = [[UIWindow alloc] initWithFrame:windowFrame]; [self setWindow:theWindow]; ... }
Build and run the application. The table view should display your test data. You still can’t add new tasks, though. Once more into the breach!