Tutorial: How to Rename a Class using Refactor in Xcode 4 and Xcode 5

Update: Although this post was originally written about Xcode 4 the procedure is the same in Xcode 5

I’ve always found it annoying that the Apple templates for Navigation-based Projects called the root view controller class RootViewController. If you create a really great view controller and then realize it would be useful in another project you have to rename the class and all the files. In XCode 3 this meant renaming the .m, .h and .xib files, using search and replace to rename the class interface and implementation sections and the using Interface Builder to make sure the MainWindow.xib and your newly renamed .xib knew about one and other.* The new refactoring functionality in XCode 4 make this much less painless.

XCode 4 Symbol Navigator

Figure 1: Symbol Navigator

To rename a class begin by switching to the symbol navigator view. You can do this by clicking the icon shown in Figure 1 or choosing View > Navigators > Symbol.

Select the class you want to rename.
Choose Edit > Refactor > Rename….
Enter the new name of the class (see Figure 2).
Click Preview.

XCode 4 Refactoring Class Renaming Action Sheet

Figure 2: New Class Name Action Sheet

The preview action sheet, shown in Figure 3, allows you to view side by side all the proposed changes to your files.

XCode 4 Refactoring Preview

Figure 3: Refactoring Preview

Click Save and you are done!

The only gripe I have is that this process failed to rename the .xib file so I had to do that manually and edit MainWndow.xib’s view controller to point to newly renamed file. Even with this slight glitch, the overall process took less than a minute and is considerably less error-prone that the manual XCode 3 method.

Footnote
* A couple of hours after publishing this article I finally found the refactor menu item in XCode 3. I will write a blog article about it soon.

Posted in Tutorial | Tagged , , , , | 3 Comments

Tutorial: Using UITableView


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

Posted in Tutorial | Tagged , , | 2 Comments

Introduction to Properties in Objective-C

Properties in Objective-C allow you to provide a well-defined interface for other classes to manipulate (i.e. get or set) attributes of a class. They also insulate external classes from the implementation details of the attributes (this separation of function and implementation is known as encapsulation).

To add a property to a class you need two parts:

  1. A property declaration
  2. An implementation or synthesis of a getter and/or setter method

The property declarations go in the interface section of your class:

@interface RootViewController : UITableViewController {
    CGFloat  mFontSize;
    NSArray* mFontNames;
}

@property (nonatomic, assign) CGFloat fontSize;
@property (nonatomic, retain) NSArray* fontNames;

@end

The general form a property declaration is:
@property (attributes) type name;
Where attributes is a comma separated list. Attributes fall into a few categories:
Writability

readwrite
the property may be written and read. This is the default.
readonly
the property may only be read.

Setter Semantics

assign
the incoming value will be assigned to the property. Use this for plain datatypes (e.g. int, double).
reatain
the incoming value will be retained. If the value is an Objective-C object this is the most common.
copy
the incoming value will be copied. This is used if there is a chance that the incoming object may change (e.g. NSMutableString).
Atomicity

nonatomic
properties are atomic by default. Use this to avoid locking.

The compiler can automatically generate (synthesis) the most common forms of getters and setter for you. You instruct it to do so using the @synthesize directive in the implementation section of your class.

@implementation RootViewController

@synthesize fontSize = mFontSize;
@synthesize fontNames = mFontNames;

// Rest of implementation

@end

The general form of the synthesize directive is:
@synthesize property_name [ = instance_variable];
In the above the square brackets indicate an optional portion. In my case the name the instance variable used to store the property was not the same as the property so I need to specify it. Had I, for example, called the mFontSize instance variable fontSize then writing @synthesize fontSize; would have been sufficient. I’ll explain why you might want adopt a naming convention for instance variable in a later post.

Reference
The Objective-C Programming Language: Declared Properties

Posted in Objective-C | 2 Comments