iPad SDK : Popovers - The Font Name Popover (part 1)

7/14/2011 3:51:23 PM
The first popover we're going to create is for choosing the font used by the Text tool. We'll simply display a list of all available font names on the device; specifying the size will be the job of our next popover. What we need here is a view controller that will let us display a list of selectable items, and this list may be larger than the screen itself, so it should be scrollable—sounds like a job for a UITableView!

Add a new class to your project, and use the assistant that comes up to specify that you want a Cocoa Touch class, specifically a UIViewController subclass. If you've ever created a UIViewController subclass for iPhone in the past—and I suspect you have—this should look pretty familiar. The main difference is the inclusion of a new Targeted for iPad check box. If that's checked, Xcode will use a slightly different template for creating your class. Go ahead and make sure that's turned on, along with the UITableViewController subclass check box, but not the XIB check box, as shown in Figure 1. Click Next, and then enter FontListController as the name of your new class.

Figure 1. Creating a new controller class

1. The Simplest Popover You'll Ever Create

Thanks to the power and flexibility of UITableView, creating this class is going to be a breeze. All we need to do is add a few instance variables for hanging onto a list of fonts as well as the current selection, and fill in a few short methods in the controller class.

Start with FontListController.h, adding the instance variables and matching properties as shown in the following code. We're also defining a string constant, which will be used to let the main view controller know that the user has selected something.

#import <UIKit/UIKit.h>
// we'll use a notification with this name, to let the main
// view controller know that something was selected here.
#define FontListControllerDidSelect @"FontListControllerDidSelect"
@interface FontListController : UITableViewController {
NSArray *fonts;
NSString *selectedFontName;
UIPopoverController *container;
@property (retain, nonatomic) NSArray *fonts;
@property (copy, nonatomic) NSString *selectedFontName;

@property (assign, nonatomic) UIPopoverController *container;

As you can see, we also created an instance variable for pointing to the UIPopoverController that acts as the container for an instance of this class. FontListController doesn't have any use for this itself, but it will be used later when DudelViewController needs to close the containing UIPopoverController.

Now switch over to FontListController.m, where we have a series of small changes to make to the default template. Apart from the changes shown here, you can leave the rest of the autogenerated class as is. First, synthesize all the declared properties by adding this line inside the @implementation FontListController section:

@synthesize fonts, selectedFontName, container;

Then uncomment the viewDidLoad method, remove most of the code in there (except for the call to [super viewDidLoad]), and add the bold lines shown here to its body:

- (void)viewDidLoad {
[super viewDidLoad];
NSArray *familyNames = [UIFont familyNames];
NSMutableArray *fontNames = [NSMutableArray array];
for (NSString *family in familyNames) {
[fontNames addObjectsFromArray:[UIFont fontNamesForFamilyName:family]];
self.fonts = [fontNames sortedArrayUsingSelector:@selector(compare:)];

In a nutshell, this goes through an array of strings containing font family names, gets all the fonts that belong to each family, and adds them to an array. Finally, it puts them in alphabetical order and saves the sorted array in the fonts instance variable.

Next, uncomment the viewWillAppear: method, and add the bold lines shown here:

- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
NSInteger fontIndex = [self.fonts indexOfObject:self.selectedFontName];
if (fontIndex != NSNotFound) {
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:fontIndex inSection:0];
[self.tableView scrollToRowAtIndexPath:indexPath
atScrollPosition:UITableViewScrollPositionMiddle animated:NO];

This code tries to find the location of the selected font in the array of fonts, and scrolls the table view to make it visible. This works under the assumption that the code that initializes this class (which we'll add to DudelViewController soon) also sets the selectedFontName property. The check against NSNotFound makes sure that we don't crash in case that value hasn't been set or is set to something invalid (a font name that isn't in our list).

Next, we fill in the blanks for the basic UITableViewDatasource methods that every UITableViewController subclass must implement:

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// Return the number of sections.
return 1;

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in the section.
return [fonts count];

// Customize the appearance of table view cells.
- (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];

// Configure the cell...
NSString *fontName = [fonts objectAtIndex:indexPath.row];
cell.textLabel.text = fontName;
cell.textLabel.font = [UIFont fontWithName:fontName size:17.0];
if ([self.selectedFontName isEqual:fontName]) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
} else {
cell.accessoryType = UITableViewCellAccessoryNone;
return cell;

The first two methods are self-explanatory, and the last one isn't much more complicated. It just sees which font name is at the specified index, and uses that name both as the display value and to look up a font. That way, each font name is displayed in its own font! It also sets a check box on the cell if (and only if) the current font name matches the selected font, so the user can see the current selection while scrolling through the list.

Next, we're going to implement the method that's called when the user selects a row. The idea is to make a note of which font the user selected, update the display of the affected rows (so the check box appears in the correct cell) to give the user some immediate feedback, and then post a notification so that whoever is listening, such as DudelViewController, will get a chance to do something. This method already exists in the template code, but contains some commented-out example code that isn't relevant here. Delete that, and add the code shown in bold:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// determine two affected table columns: the one that was selected before,
// and the one that's selected now.
NSInteger previousFontIndex = [self.fonts indexOfObject:self.selectedFontName];

// don't do any updating etc. if the user touched the already-selected row
if (previousFontIndex != indexPath.row) {
NSArray *indexPaths = nil;
if (previousFontIndex!= NSNotFound) {
NSIndexPath *previousHighlightedIndexPath = [NSIndexPath
indexPathForRow:previousFontIndex inSection:0];
indexPaths = [NSArray arrayWithObjects:indexPath, previousHighlightedIndexPath, nil];
} else {
indexPaths = [NSArray arrayWithObjects:indexPath, nil];

// notice the new selection
self.selectedFontName = [self.fonts objectAtIndex:indexPath.row];

// then reload
[self.tableView reloadRowsAtIndexPaths:indexPaths
[[NSNotificationCenter defaultCenter] postNotificationName:FontListControllerDidSelect

Finally, we need to add a bit of cleanup, so that the list of font names doesn't hang around forever:

- (void)viewDidUnload {
// relinquish ownership of anything that can be re-created in viewDidLoad or on demand.
// For example: self.myOutlet = nil;
self.fonts = nil;

- (void)dealloc {
self.fonts = nil;
self.selectedFontName = nil;
[super dealloc];

That should be all we need for the FontListController class itself. At this point, you should try to build your app, just to make sure no syntax errors have snuck in, but you won't see any difference when you run the app just yet. Our next step here will be enabling DudelViewController to use our new class.

  •  Beginning Android 3 : Working with Containers - Tabula Rasa
  •  Beginning Android 3 : Working with Containers - LinearLayout Example & The Box Model
  •  iPhone Application Development : Reading and Writing User Defaults (part 2) - Implementing System Settings
  •  iPhone Application Development : Reading and Writing User Defaults (part 1) - Creating Implicit Preferences
  •  - Mobile Application Security : SMS Security - Overview of Short Message Service
  •  - Mobile Application Security : Bluetooth Security - Bluetooth Security Features
  •  Integrating Your Application with Windows Phone 7
  •  Introducing Windows Phone 7 Photo Features (part 2) - Using a Chooser to Open Photos & Saving Photos to the Phone
  •  Introducing Windows Phone 7 Photo Features (part 1) - Using a Chooser to Take Photos
  •  Mobile Application Security : Bluetooth Security - Bluetooth Technical Architecture
  •  Mobile Application Security : Bluetooth Security - Overview of the Technology
  •  Windows Phone 7 Development : Push Notifications - Implementing Cloud Service to Track Push Notifications
  •  Windows Phone 7 Development : Push Notifications - Implementing Raw Notifications
  •  Windows Phone 7 Development : Push Notifications - Implementing Tile Notifications
  •  Windows Phone 7 Development : Push Notifications - Implementing Toast Notifications
  •  iPhone Application Development : Creating a Navigation-Based Application
  •  Windows Phone 7 Development : Push Notifications - Introducing the Push Notifications Architecture
  •  Windows Phone 7 Development : Push Notifications - Understanding Push Notifications
  •  Windows Phone 7 Development : Handling Multiple Concurrent Requests with Rx.NET
  •  WAP and Mobile HTML Security : Application Attacks on Mobile HTML Sites
    Top 10
    Nikon 1 J2 With Stylish Design And Dependable Image And Video Quality
    Canon Powershot D20 - Super-Durable Waterproof Camera
    Fujifilm Finepix F800EXR – Another Excellent EXR
    Sony NEX-6 – The Best Compact Camera
    Teufel Cubycon 2 – An Excellent All-In-One For Films
    Dell S2740L - A Beautifully Crafted 27-inch IPS Monitor
    Philips 55PFL6007T With Fantastic Picture Quality
    Philips Gioco 278G4 – An Excellent 27-inch Screen
    Sony VPL-HW50ES – Sony’s Best Home Cinema Projector
    Windows Vista : Installing and Running Applications - Launching Applications
    Most View
    Bamboo Splash - Powerful Specs And Friendly Interface
    Powered By Windows (Part 2) - Toshiba Satellite U840 Series, Philips E248C3 MODA Lightframe Monitor & HP Envy Spectre 14
    MSI X79A-GD65 8D - Power without the Cost
    Canon EOS M With Wonderful Touchscreen Interface (Part 1)
    Windows Server 2003 : Building an Active Directory Structure (part 1) - The First Domain
    Personalize Your iPhone Case
    Speed ​​up browsing with a faster DNS
    Using and Configuring Public Folder Sharing
    Extending the Real-Time Communications Functionality of Exchange Server 2007 : Installing OCS 2007 (part 1)
    Google, privacy & you (Part 1)
    iPhone Application Development : Making Multivalue Choices with Pickers - Understanding Pickers
    Microsoft Surface With Windows RT - Truly A Unique Tablet
    Network Configuration & Troubleshooting (Part 1)
    Panasonic Lumix GH3 – The Fastest Touchscreen-Camera (Part 2)
    Programming Microsoft SQL Server 2005 : FOR XML Commands (part 3) - OPENXML Enhancements in SQL Server 2005
    Exchange Server 2010 : Track Exchange Performance (part 2) - Test the Performance Limitations in a Lab
    Extra Network Hardware Round-Up (Part 2) - NAS Drives, Media Center Extenders & Games Consoles
    Windows Server 2003 : Planning a Host Name Resolution Strategy - Understanding Name Resolution Requirements
    Google’s Data Liberation Front (Part 2)
    Datacolor SpyderLensCal (Part 1)