This is a brief tutorial on how to use UITableView. Instead of populating the table with a list of hard-coded strings, I would like to show you how to do something (slightly) more useful: list all the fonts available on your iPhone. This can be quite handy if you are trying to choose a font and want to see what it will look like on your device. All the source is available for download from the link at the end of the article.
Setting Up the Project
In XCode choose File > New Project… and choose Navigation-based Application and press Next. When prompted for a name for the new project use FontInfo. Of course, you could use any name you like, but it will be easier to follow along if you use the same names as I do. If you are using XCode 3 click Save. If you are using XCode 4 click Next, choose where you want your project to be created and click Create . A this point you should have a new project containing two classes RootViewController
and FontInfo1AppDelegate
.
Adding Properties
To store the information for you are going to add two properties to the RootViewController
. Open the file RootViewController.m
and add the highlighted lines:
@interface RootViewController : UITableViewController { CGFloat mFontSize; NSArray* mFontNames; } @property (nonatomic, assign) CGFloat fontSize; @property (nonatomic, retain) NSArray* fontNames; @end
Initializing the Properties
In UIKit when a view had been successfully loaded the method viewDidLoad
is called. This is where you will add code to initialize the newly added properties. Open the file RootViewController.m
and add the following:
- (void)viewDidLoad { [super viewDidLoad]; // Set the title in the navigation bar self.title = @"Available Fonts"; // Get the list of font family names and from that // build the list of all font names in tempFontNames NSArray* familyNames = [UIFont familyNames]; NSMutableArray* tempFontNames = [[NSMutableArray alloc] init]; for(NSString* familyName in familyNames) { [tempFontNames addObjectsFromArray:[UIFont fontNamesForFamilyName:familyName]]; } self.fontNames = tempFontNames; [tempFontNames release]; // Match the system font self.fontSize = [UIFont systemFontSize]; }
Memory Management
In viewDidLoad
you added code that causes your view controller to retain memory. Before going any further you should ensure that you add code to release that memory, otherwise you will end up leaking memory.
UIKit calls viewDidUnload
when your view is no longer needed and the Objective-C runtime calls dealloc
when you view controller is not longer needed. Make the follow changes to these methods:
- (void)viewDidUnload { self.fontNames = nil; } - (void)dealloc { [mFontNames release]; [super dealloc]; }
Providing the Data to the View
UITableView calls a number of methods to obtain the data to display:
numberOfSectionsInTableView
tableView:numberOfRowsInSection
tableView:cellForRowAtIndexPath
You will find these methods in RootViewController.m
.
Your table view has only one section and the default implementation of numberOfSectionsInTableVIew
returns 1, so you do not need to change this method.
The number of rows in your table is the number of font names in the self.fontNames
array. Make the following changes to tableView:numberOfRowsInSection
:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { NSAssert(self.fontNames != nil, @"Illegal nil self.familyNames"); return [self.fontNames count]; }
This should all be fairly self-explanitory except perhaps for the call to NSAssert
. In Objective-C, unlike C++ or Java, it is harmless to call a method on the nil
object. If self.fontNames had not been initialized then [self.fontNames count]
would return 0 with no error and the table view would simply show no data. It might take you some time to figure out what was wrong. In above code, however, if the first argument to NSAssert
is not YES
then the program will crash and the second argument will be printed on the debug console.
UITableView calls tableView:cellForRowAtIndexPath:
for each visible row to obtain the data to display. Modify this method as shown:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *CellIdentifier = @"Cell"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; if (cell == nil) { cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease]; } NSAssert(self.fontNames, @"Illegal nil self.familyNames"); NSString* fontName = [self.fontNames objectAtIndex:indexPath.row]; UIFont* font = [UIFont fontWithName:fontName size:self.fontSize]; cell.textLabel.font = font; cell.textLabel.text = fontName; return cell; }
Lines 2-7 in the above code are generated by XCode and are used to improve the scrolling performance. Basically when a cell is no longer used it is put into a pool of cells for reuse. It is faster to reuse an existing cell than allocate a new one, so the code first tries to find a reusable cell and only if that fails does it allocate a new cell.
Lines 9-13 contain the code you added. After checking that self.fontNames
is valid, the fontName for the row is retrieved. The font name is used to create a matching font. Finally the cell’s text label is set to display the font name using the corresponding font.
If all went well you should now be able to run the tutorial. If you are having difficulty you might want to download the source from the link below and compare it to your work.
Download Tutorial Source
FontInfo1
Thanks for the tutorial. Any chance of one on drill-down views?
Thanks Sammy! You have the dubious honor of being my first registered user.
As for a tutorial on a detail view controller, I am working on it. Turns out writing these tutorials takes quite a bit of time. (I can knock out the code pretty quickly; it’s the bits for the humans that are tricky)
Cheers, idz