Now that we’ve built the model, we have to go back to the RootController class and build the bridge
between the view controller and the model. To do this we need to make only one change in the RootController interface declaration
(RootController.h). Add a pointer to an NSMutableArray that you’ll then populate inside
the viewDidLoad: method:@interface RootController : UIViewController
<UITableViewDataSource, UITableViewDelegate> {
UITableView *tableView;
NSMutableArray *cities;
}
Changes to the implementation
(RootController.m) are only slightly more extensive.
You need to #import both the
City.h and CityGuideDelegate.h
interface files, as you’ll be using both of these classes inside the
updated implementation:
#import "RootController.h"
#import "CityGuideDelegate.h"
#import "City.h"
As I mentioned earlier, you must implement the viewDidLoad: method. This
UIViewController method is called after
the controller’s view is loaded into memory, and is the method we’ll
normally use to set up things that the view needs. You’ll find that the
Xcode template included a stub for viewDidLoad (not far from the #pragma mark-labeled instance methods), but it’s
commented out (wrapped inside a comment, so it
doesn’t compile). Replace it with the following (be sure to remove the
/**/ so that it’s no longer commented out): and
- (void)viewDidLoad {
CityGuideDelegate *delegate =
(CityGuideDelegate *)[[UIApplication sharedApplication] delegate];
cities = delegate.cities;
}
Here, we acquired a reference to the application delegate by using
the [[UIApplication sharedApplication]
delegate] method call. Since this method returns a generic
id object, we had to cast it to be a
CityGuideDelegate object before
assigning it. We then grabbed a pointer to the array of cities managed by
the app delegate.
Since our code now declares a new variable, we also have to remember
to release it in the dealloc:
method:
- (void)dealloc {
[tableView release];
[cities release];
[super dealloc];
}
Finally, we must use the model to populate the table view. The
number of rows in the table view should now be determined by the number of
cities in the NSMutableArray instead of
simply returning “3” all the time. We must now go ahead and change the
tableView:
numberOfRowsInSection: method to reflect that by replacing the line return 3; (and the comment above it). Here’s how
the method should look now:
- (NSInteger)tableView:(UITableView *)tv
numberOfRowsInSection:(NSInteger)section
{
return [cities count];
}
Finally, we need to change the tableView:cellForRowAtIndexPath: method to label
the cell with the correct city name. To do this, add the following code
shown in bold, which figures out which row of the table we’re trying to
populate and looks up the appropriate element of the cities array:
- (UITableViewCell *)tableView:(UITableView *)tv
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell =
[tv dequeueReusableCellWithIdentifier:@"cell"];
if( nil == cell ) {
cell = [[[UITableViewCell alloc]
initWithFrame:CGRectZero reuseIdentifier:@"cell"] autorelease];
}
City *thisCity = [cities objectAtIndex:indexPath.row];
cell.textLabel.text = thisCity.cityName;
return cell;
}
We’ve now reached a point where we have a functional, buildable
application. However, while our table view now reflects our model, we
still can’t access any of the information we entered about our cities.
When we click on a city we want the application to tell us about the city,
and for that we need to modify the tableView:didSelectRowAtIndexPath: method. But
for now, click the Build and Run button on the Xcode toolbar, and your
iPhone Simulator should pop up, looking like Figure 1.
1. Mocking Up Functionality with Alert Windows
Before I go on to show how to properly display the city descriptions and images using the UINavigationController
class, let’s do a quick hack and get the application to pop up an alert
window when we click on a table view cell. Go back to
RootController.m and add the highlighted lines in
the following code to the didSelectRowAtIndexPath: method:
- (void)tableView:(UITableView *)tv
didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
City *thisCity = [cities objectAtIndex:indexPath.row];
UIAlertView *alert = [[UIAlertView alloc]
initWithTitle:thisCity.cityName message:thisCity.cityDescription
delegate:self cancelButtonTitle:nil otherButtonTitles:@"OK", nil];
[alert show];
[alert autorelease];
[tv deselectRowAtIndexPath:indexPath animated:YES];
}