Showing posts with label cocoa-touch. Show all posts
Showing posts with label cocoa-touch. Show all posts

July 6, 2009

Changing the Accessory View

In my application, I want to replace the button in the accessory view area of a table cell with my own button. I want it to look like this:
Clicking on the cell should produce the callback tableView:(UITableView *) aTableView didSelectRowAtIndexPath:(NSIndexPath *) indexPath. However, when the pencil icon is hit, I want it to do a different callback. I didn't want to make a custom table cell class, and from reading the documentation, it appeared relatively straight forward to use the existing table cell class. All I had to do was set the cell property accessoryView to something else. Here is what the SDK documentation says:
@property(nonatomic, retain) UIView *accessoryView Discussion If the value of this property is not nil, the UITableViewCell class uses the given view for the accessory view and ignores the value of the accessoryType property. The provided accessory view can be a framework-provided control or label or a custom view. The accessory view appears in the the right side of the cell. If the cell is enabled (through the UIView property userInteractionEnabled) , the accessory view tracks touches and, if tapped, sends the accessory action message set through the accessoryAction property.
Ok, sounds simple enough, so I went ahead and coded up what I thought was a trivial solution. I picked a UIImageView to put into the accessoryView. I was careful about setting the userInteractionEnabled flag and everything. Here is the snippet of code I came up with (which belongs in the cellForRowAtIndexPath method, in the case we have to create a new UITableCell object):
// stick the pencil icon on the accessory view
UIImageView* pencil = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"icon-pencil.gif"]];
pencil.userInteractionEnabled = YES;
pencil.frame = CGRectMake(0, 0, 15, 15);
cell.accessoryView = pencil;
cell.accessoryAction = @selector(didTapEditButton:); 
Well, this didn't work. All taps on the image produced the didSelectRowAtIndexPath callback. I re-read the documentation on accessoryView. That last sentence mentions the accessoryAction property, so I thought I should take a peek at that:
@property(nonatomic) SEL accessoryAction Discussion If you specify a selector for the accessory action, a message is sent only if the accessory view is a detail disclosure button—that is, the cell's accessoryType property is assigned a value ofUITableViewCellAccessoryDetailDisclosureButton. If the value of this property is NULL, no action message is sent. The accessory view is a UITableViewCell-defined control, framework control, or custom control on the right side of the cell. It is often used to display a new view related to the selected cell. If the accessory view inherits from UIControl, you may set a target and action through the addTarget:action:forControlEvents: method. See accessoryView for more information.
Hmmm. That very first sentence says I can't do this with a custom view. But the next paragraph states I can just use the addTarget:action:forControlEvents: method. (Then it makes a circular reference back to accessoryView for more info, which doesn't offer more info.) But a UIImageView is not derived from UIControl, so that is not available. The solution to this mess is to use a UIButton instead of a UIImageView. UIButtons are UIControls, so the final code looks like this:
// stick the pencil icon on the accessory view
UIButton* pencil = [UIButton buttonWithType:UIButtonTypeCustom];
[pencil setImage:[UIImage imageNamed:@"icon-pencil.gif"] forState:UIControlStateNormal];
pencil.frame = CGRectMake(0, 0, 15, 15);
pencil.userInteractionEnabled = YES;
[pencil addTarget:self action:@selector(didTapEditButton:) forControlEvents:UIControlEventTouchDown];
cell.accessoryView = pencil; 
This time it works. I get the desired callback when the pencil icon is touched. The summary is: use a UIControl based widget in the accessoryView, and ignore the accessoryAction property.

May 29, 2009

Trapping the UINavigationBar Back Button (part 3)

I have covered the topic of trapping the back button in order to do something. These previous forays are part 1 and part 2. I had implemented my solution from part 2, and it is working well. However, I am always on the look out for a better, cleaner solution. Just recently, an anonymous commenter pointed out something I had not noticed before: the UINavigationBarDelegate prototype has a method called navigationBar:shouldPopItem: It is called just before the item is popped. If it returns NO, then the pop won't actually happen. But this can be the perfect way to insert some logic before the pop happens (and return YES at the end). Here is how you would implement your back-button press event logic:
- (BOOL)
navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item
{
   //insert your back button handling logic here

   // let the pop happen
   return YES;
}  
This is so much cleaner than my previous solution. I love it! Thanks to whomever pointed out the obvious.

May 20, 2009

A Background Task Class

I am working on an app that makes XML calls to a web service. At first, I made the calls within the main thread and waited for the results to come back. Not surprising, when these calls took longer than usual, the user interface suffered. Basically, the UI locks up while waiting for a response from the web service. This is because the main thread is busy and cannot service UI-related events, such as touch events. What I had to do was make the web service calls in a separate thread so that the main thread can continue to service the UI. I coded up a few uses and I quickly discovered that my app wanted two different behaviors while making web service calls in the background. These behaviors are:
  1. The background task should operate transparently. It would only alert the user if the operation timed out,
  2. The user had to wait for the operation to complete, but user is given the option to cancel the background operation.
I decided to create a utility class to encapsulate these behaviors and make it all a little easier. Thus I created the BackgroundTask class, which I present here. What does it do? The BackgroundTask class is like a NSThread class; it allows you to spin off code in a separate thread. But this class is more powerful because it supports the two behaviors described above.

The Behaviors

First, let's discuss the desired behaviors in a little more detail. Behavior #1 listed above is for tasks that are low priority and don't require the user to wait for it to complete. The task is kicked off in the background, and the main thread retains control for the user to do whatever they need. They don't even know there is something going on in the background (except for the spinning network access icon in the status bar). The only feedback the user may get is if the operation timed out, meaning it failed. Each task created is given a timeout value. If the operation is not complete by this timeout period, then an alert message pops up that displays something like this:
The alert message displayed below "Operation failed" is also configurable when you create the task object. Behavior #2 is more of a blocking operation, but with the option to cancel it if the user feels it is taking too long. This is useful for higher-priority tasks that require the user to wait until it is complete. For example, if the user hit a refresh button to update the items displayed in a table, it kind of makes sense for the refresh to complete before continuing. When running a task like this, it displays a modal dialog like this:
I added a little optimization to this behavior as well. This dialog won't pop up for X number of seconds, where X is configurable. This is to avoid those situations where the background task completes almost immediately. When this happens, it was silly for the alert to pop up then disappear so fast that the user couldn't even read what it said. So the alert will only come up if the task takes longer than X seconds.

Instantiating

Since the BackgroundTask class supports two different behaviors, it also supports two different initializers. This first one initializes the object for behavior #1, where the task runs the background without any user interaction:
BackgroundTask* task = [[BackgroundTask alloc] initWithTarget:self
   selector:@selector(executeEmailPage:)
   argument:myparent.page
   timeout:40.0
   alertMsg:@"Attempt to email page timed out"]; 
The target/selector arguments specify the method to run in the new thread. The argument is optional data you can pass to the above method. Timeout specifies how long to give that thread before putting up an alert. And the final argument is the alert message to put up when the operation times out. This next initializer is for behavior #2, where the user can cancel the operation:
BackgroundTask* task = [[BackgroundTask alloc]
   initWithTarget:self
   selector:@selector(backgroundRefresh)
   argument:nil
   waitBeforeAlert:0.5
   title:@"Refreshing data"
   msg:@"Standby while refreshing pages"]; 

The target, selector, and argument parameters are the same as the first initializer. The waitBeforeAlert parameter specifies how long to give the operation to complete before putting up the modal box. The title and msg parameters allow you to configure the text displayed in the modal dialog.

Usage

To use the BackgroundTask object after you create it is very simple:
// start the background thread
[task start];

// release it since we don't need it anymore
[task release]; 

Getting The Code

The code for the BackgroundTask class is available on the Osmorphis google group. You can download the .zip archive here. Hopefully its usage is clear enough that you can easily stick the code in your own project and begin using it right away.

May 12, 2009

Multiple Buttons on a Navigation Bar

Previously, I wrote about how to use standard icons in your navigation bar. This takes advantage of the nav bar's ability to put a button on the right side. But what if you want to put multiple buttons there? If your title is short enough, you can easily fit 2 or 3 buttons in that space. The nav bar allows you to stick content on the right hand side via the rightBarButtonItem property. On the surface, you might think you can add but one button to this property. But it's not so. There is an initializer for UIBarButtonItem that takes a view:
initWithCustomView:(UIView *)customView
With this, you can create multiple UIButton objects, add them to a UIView, and then create a UIBarButtonItem passing in this parent UIView. The following pseudo-code shows what I mean:
// create the container
UIView* container = [[UIView alloc] init];

// create a button and add it to the container
UIButton* button = [[UIButton alloc] init...];
[container addSubview:button];
[button release];

// add another button
button = [[UIButton alloc] init...];
[container addSubview:button];
[button release];

// now create a Bar button item
UIBarButtonItem* item = [[UIBarButtonItem alloc] initWithCustomView:container];

// set the nav bar's right button item
self.navigationItem.rightBarButtonItem = item;
[item release];

But, what if you want multiple *standard* buttons. Access to the standard buttons/icons is through the UIBarButtonItem class, not UIButton. You can't add UIBarButtonItem objects to a UIView. While there is support to create a UIBarButtonItem object from a UIView-derived object, there does not seem to be a way to do the opposite; there is no way to create a UIView object from a UIBarButtonItem. The only way I found how to deal with multiple UIBarButtonItem objects is to stick them in a toolbar. A UIToolBar is designed to hold multiple UIBarButtonItem objects. Think of a toolbar as your container, instead of the UIView object. And since a UIToolBar is UIView based, it can be added to the right side of a nav bar using the above trick. The following code shows how you can add two standard buttons to the right side:

// create a toolbar to have two buttons in the right
UIToolbar* tools = [[UIToolbar alloc] initWithFrame:CGRectMake(0, 0, 133, 44.01)];

// create the array to hold the buttons, which then gets added to the toolbar
NSMutableArray* buttons = [[NSMutableArray alloc] initWithCapacity:3];

// create a standard "add" button
UIBarButtonItem* bi = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:NULL];
bi.style = UIBarButtonItemStyleBordered;
[buttons addObject:bi];
[bi release];

// create a spacer
bi = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil];
[buttons addObject:bi];
[bi release];

// create a standard "refresh" button
bi = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemRefresh target:self action:@selector(refresh:)];
bi.style = UIBarButtonItemStyleBordered;
[buttons addObject:bi];
[bi release];

// stick the buttons in the toolbar
[tools setItems:buttons animated:NO];

[buttons release];

// and put the toolbar in the nav bar
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:tools];
[tools release];


May 4, 2009

Using Standard Toolbar and Navbar Buttons

In an article several months back, I discussed how to use standard Tab bar buttons. These are icons provided by Apple for you to use in the tab bar. But tab bars are not the only container that offers system-provided icons. There are standard icons available for toolbars and navigation bars. Recently I wanted to make use of a system icon, but it took me some time to find what icons were standard and how to use them. Even though it is fairly trivial, I would like to share this information, if nothing else but to save people a few minutes of needless searching. If you feel weird about using system-provided icons, don't. Apple is very keen on developers making use of the standard icons and encourages it in the HIG. However, Apple is also adamant about the consistent use of these icons. If your application uses a standard icon in a way unintended for that icon, then it is likely your app will get rejected from the app store. For example, if you are using the standard Contacts icon to bring up a list of contact lens providers, ding! Here is the section in the HIG that lists the standard icons and their intended uses. This gives you an idea to what is available, but doesn't help you coding one of them up. You have to know how to reference each of these in your code. For this, you have to look at the UIBarButtonItem class reference. If you go to the Constants section, you will see the UIBarButtonSystemItem enumerations which define the values for all the system icons.

For nav bars

To put a system icon on the navigation bar, you have several choices for the location. At the most basic level, you can put the icon on the left or the right. If you require more refinement, it's quite easy to do with subviews, but that technique is well-documented and outside the scope of this article. Here is the simple code for putting the standard refresh icon in the left position:
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc]
                                      initWithBarButtonSystemItem:UIBarButtonSystemItemRefresh
                                      target:self
                                      action:@selector(refresh:)];

The first parameter is the enum from the UIBarButtonItem reference. The target/action parameters specify the method to call when the button is hit.

For toolbars

Adding the standard icons to a toolbar is a little more involved, but still straight-forward. Toolbars also take UIBarButtonItem objects, but instead of assigning them to specific locations, you must add them to an NSArray object first (in the order you want them) and then add the array to the toolbar object.

// create the toolbar object
UIToolBar* toolbar = [[UIToolBar alloc] init];

// create an array to hold the buttons
NSMutableArray* buttons = [[NSMutableArray alloc] init];

// create a standard button
UIBarButtonItem* button = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemRefresh
target:self
action:@selector(refresh:)];

// stick it in the array
[buttons addObject:button];

// add more buttons
//...

// add the array to the toolbar
toolbar.items = buttons;

// done with the buttons array
[buttons release];

April 28, 2009

Invoking Selectors

There are many instances in the Cocoa-Touch SDK where you need to specify a callback method. For example, UIButton has a method where you set the callback for when the button is hit. Or an NSTimer can have a callback for when the timer expires. UIBarButtonItem also has a callback when the button is hit. Every one of these examples follows the same pattern: you have to provide the target (usually self, but doesn't have to be), and a selector created with the mysterious @selector() construct. Wouldn't it be neat if you could use these target/selector pairs in your own code? There are many possibilities of why you would want to implement your own callbacks, like writing your own GUI buttons, or you wanted to create a convenience class to make threading a little easier. Let's demonstrate using that last example: create a convenient Thread wrapper class. This class is very similar to the NSOperation class, so this is really for demonstration purposes and not something terribly useful.
The NSThread class requires you to provide the target/selector for the function's main entry point. That method must create a new autorelease pool, and then release it when it is done. It would be nice if we didn't have to do that boilerplate code every time we wanted to spawn off a thread. So a wrapper class can do it for us. Let's call this new class MyThread:
// MyThread.h

@interface MyThread
{
NSThread* thread;
NSInvocation* inv;
}

@property(nonatomic,retain) NSThread* thread;
@property(nonatomic,retain) NSInvocation* inv;

- (id) initWithTarget:(id)target selector:(SEL) selector object:(id) argument;
- (void) start;

@end

Obviously, this class doesn't do as much as the NSThread class, but these two methods are sufficient to demonstrate method invocation. What this class does is create the NSThread object, then provide the thread main entry point, which points back into the MyThread class. This will create the autorelease pool, then call the "other" entry point (the one that was supplied to MyThread). When that returns, it releases the pool. The key here, and the point of this article, is calling the other entry point. It is not trivial, as you'll soon see. Most of the grunt work is done in the MyThread initializer:

- (id) initWithTarget:(id)target selector:(SEL) selector object:(id) argument
{
  // create the thread object
  thread = [[NSThread alloc] initWithTarget:self selector:@selector(threadMain) object:nil];

  // create the invocation object
  //
  // first we need the method signature
  NSMethodSignature* sig = [target methodSignatureForSelector:selector];

  // now create the invocation object from the signature
  NSInvocation* inv = [NSInvocation invocationWithMethodSignature:sig];

  // setup the invocation
  [inv setTarget:target];
  [inv setSelector:selector];

  // set the argument, if there is one
  if(argument) {
      [inv setArgument:&argument atIndex:2];
  }

  [inv retainArguments];
}

If you are familiar with Java, you'll notice that the NSInvocation class is similar to the java Method class in the reflection package. The NSInvocation object is what you use to piece together a method call at runtime. Once it's all put together, you can invoke it. This code first constructs a NSMethodSignature object which is used to create the NSInvocation object. Then we configure the target and selectors (not sure why, since the signature should have all that embedded). Finally, we set the arguments to the call. The argument goes at index 2 because the self argument occupies index 0, and the _cmd argument takes index 1. Thus, any additional arguments start at index 2. Now everything is all setup in our MyThread class. All we do is wait for the thread to be started via the start method. This just delegates the start to the internal NSThread object:
- (void) start
{
   [thread start];
}

The thread is configured to use the thread entry point threadMain defined in MyThread. This is where we put the boilerplate pool code, then call the callback supplied to us in the initializer.
- (void) threadMain
{
    // create the autorelease pool
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

    // now run the selector provided to us
    [inv invoke];

    // release the pool
    [pool release];
}

The NSInvocation class is a little clumsy to set up, but it can be quite powerful when you want to specify and use your own callbacks.

April 22, 2009

TableFormEditor v1.4: text field settings

A while ago, I introduced a reusable class for producing editable forms. This article can be read here. There was also a followup article containing a detailed usage example and some bug fixes in the TableFormEditor code. I have been using this class since then, and it quickly became clear that I needed a way to configure the text fields that were used in the form. For instance, one of the fields in my app takes a URL as input, and I would have liked to use the special keyboard that is designed for that type of input. This forced me to do some research on how to configure text fields. This research culminated into these two articles: part 1 and part 2. Now it is time to put this research into the TableFormEditor class. I came up with a solution that may not be the best, but since I need to make some progress on my app, I forged ahead and implemented this solution. Comments are appreciated on other ways to do this. It currently serves its purpose and might even be deemed "clever". If you are familiar with the TableFormEditor class, you know that you provide the field labels in an array. These labels are also used as keys to a dictionary that contains the field data. The edited data is also returned in a dictionary, again keyed by the row labels. Even if you don't use the field rows as labels, you still need to provide those for keying purposes. I extended this idea by allowing you to provide text field properties for each field. These properties are set to configure the behavior of the text field, as well as the keyboard behavior. All the supported properties are described in the two-part article mentioned above. I first thought about using a dictionary to hold the properties, or creating a new class, but decided to use a real UITextField object to hold the properties. After all, this class already has all the properties I want. What you do is create a UITextField object, set all the properties you need, and put this object in a dictionary. The field labels provide the dictionary keys, just as they do for the field data dictionary. It's important to understand that these UITextField objects you create are not used in the GUI; they are only used to hold values for properties, then they are thrown away. In this sense, they serve as "prototype" UITextField objects. The reason I chose to do it this way was based purely on convenience. There was no sense creating another class with all the same properties. And when setting the properties, XCode knows what the properties and it does the code completion for you. I have modified the TableFormEditor class to have an additional property: textfieldProto This is a mutable dictionary. The keys are the label names of each field. The value for each key is a UITextField object that has all the desired properties set. I have modified the example to have 4 fields:
  1. name field. For this field I would like auto correction turned off, but would like to auto capitalize the beginning of each word.
  2. age. This field is all numbers, so I would like the numeric keypad to come up when this field has the focus.
  3. homepage. For this, I want the URL keyboard, and turn off any auto capitalization and auto correction.
  4. password. This field should use the secure key feature, which dots out each letter as you type it.
The following is the code snippet from the example that demonstrates how you create the UITextField prototype objects and set them in the TableFormEditor object:

 
   // create the dictionary to hold the text field properties
   NSMutableDictionary* textFieldProto = [[NSMutableDictionary alloc] initWithCapacity:4];
  
   // prototype for the Name field
   // shrink the font to fit, don't autocorrect, autocap first letter
   UITextField* tf = [[UITextField alloc] init];
   tf.adjustsFontSizeToFitWidth = YES;
   tf.minimumFontSize = 7.0;
   tf.autocorrectionType = UITextAutocorrectionTypeNo;
   tf.autocapitalizationType = UITextAutocapitalizationTypeWords;
 
   // add it to the dictionary
   [textFieldProto setObject:tf forKey:@"Name"];
   [tf release];
  
   // prototype for the Age field
   // use the numeric keyboard
   tf = [[UITextField alloc] init];
   tf.keyboardType = UIKeyboardTypeNumberPad;
  
   [textFieldProto setObject:tf forKey:@"Age"];
   [tf release];
  
   // prototype for the Homepage field
   // use the URL keyboard, turn off autocorrect, show clear button, autocap off
   tf = [[UITextField alloc] init];
   tf.keyboardType = UIKeyboardTypeURL;
   tf.autocorrectionType = UITextAutocorrectionTypeNo;
   tf.autocapitalizationType = UITextAutocapitalizationTypeNone;
   tf.clearButtonMode = UITextFieldViewModeAlways;

   [textFieldProto setObject:tf forKey:@"Homepage"];
   [tf release];
  
   // prototype for the Password field
   // use secure entry
   tf = [[UITextField alloc] init];
   tf.secureTextEntry = YES;
  
   [textFieldProto setObject:tf forKey:@"Password"];
   [tf release];

   // now register all the text field prototypes
   form.textFieldProto = textFieldProto;
  
The updated example XCode project is available for you to play around with, as well as the updated TableFormEditor class to support text field prototypes,  using this link.

April 15, 2009

Creating Stretchable Button Images

In a previous article, I described a couple techniques for putting buttons in a table. If you want your buttons to have a background color, and likely some kind of gradient, the trick is to use a custom UIButton. These are interesting because they can have an image that forms the background of the button, and this image can be stretched to fits the frame of the button. In this article I will describe how to create these stretchable button images that look nice in a table. I will demonstrate this using Inkscape, a wonderful vector drawing application that can be freely downloaded from here. What I will show is how to create an image that looks like this:
When this image is applied to a custom UIButton, with some text added, you get something like this:
This shows how the image is stretched in order to fit the button's frame.

Step 1. Starting Inkscape

The first step is to fire up Inkscape. We're only dealing with an image of around 100x40 pixels, so your canvas shouldn't be too big. Bring up the document properties (File>Document Properties) and set the custom size to something like 200 x 60 pixels. But this will make your canvas really small, so zoom in with View>Zoom>Page
Here is what your Inkscape window should look like:

Step 2. Drawing the Rectangle

Now we will use the rectangle tool to draw our image. I don't think the size of the image is terribly important, since Cocoa Touch will stretch it to fit. For this exercise, I will create an image roughly 90x40 pixels. Click on the rectangle and squares tool (F4). Now draw a rectangle on your canvas about 90x40 pixels in size. The size will be displayed in the status bar at the bottom and will adjust as you drag your mouse. Don't worry about fill or stroke color at this point; we'll adjust those later. Now you might have something like this (fill color uses whatever color you used last):

Step 3. Rounding the Corners

Time to make the rounded corners. If you don't see the little circle in the upper right corner of your rectangle, select the Edit Paths by Node tool (F2) and click on your rectangle. That should bring up the little circle. Now drag that circle down, which creates the rounded corners. Make them as round as you want, but I pulled the circle down about a ¼ of the way down:

Step 4. Filling with Color

Now we have to change the fill color. Bring up the Object>Fill and Stroke window. You may have to make the window a little larger to fit it on. Make sure the Fill tab is selected and click on the Flat Color icon at the top (the one to the right of the "X"). Now you can enter RGB values, or use the wheel, or any of the 5 methods to set the color just the way you want. I prefer to use the wheel to get to the general color, then switch over to RGB to fine tune it. For this example, I want a color with RGB values 0, 189, 0. Now my image looks like this:

Step 5. Coloring the Border

To set the stroke, you can make this whatever you want, but I prefer a stroke weight of 0.5 pixels. This allows the button to look more like a table row. This is set on the Stroke Style tab of the Fill and Stroke window. The stroke color is set on the Stroke Paint tab. I want a sort of gray color to match the table row borders. I think I am pretty close with these RGB values: 63, 118, 83:

Step 6. Adding the Glare

We're in the home stretch now, but this is the trickiest part: applying the gradient to give it that shining light affect. For this we will use the Create and edit gradients tool (above the eye-dropper tool on the left, or Ctrl-F1). Click in the middle of your image and drag the mouse up. This will create a gradient that gradually gets lighter as you go up. You can click on the endpoints and move them around to get your desired gradient affect:
You might want to add another point on the gradient line to give a more enhanced affect. To do this double click on the bottom handle. You'll see a dialog like this:
Click on the "Add stop" button, and this will add another handle on the gradient line in the middle. You can also drag this around. While the gradient editor is up, you can adjust the colors of each handle.
One problem is that the default settings for the gradient tool is to go towards a translucent color. This might look fine within Inkscape, but when you create a button with translucence, it will actually show the background through the button. This may or may not be a desired affect. If it isn't, you can change the color of each end of the gradient. Simply click on the handles and make sure the opacity is set to 100% and the color will now be solid. Unfortunately, this has the affect of hiding your gradient. Don't worry; just select each handle and adjust the colors the way you want them look. After fiddling with colors of each of the handles, I got an end product that looks like this:

Step 7. Saving your Work

Now you need to save it. This is a very confusing part of the Inkscape interface. When exporting a bitmap, Inkscape saves it as a PNG file, the perfect format for the iPhone. Select File>Export Bitmap. Make sure the Selection tab is selected and that the entire image is selected. Click on the Browse button. The browse button brings up another dialog where you select the directory and enter a filename. There is a save button on this dialog, but don't be fooled. This does not save the file. It really behaves like an Ok button, so go ahead and click it. Now you can actually save the file by hitting the Export button. Copy the resulting .png file into your XCode project, and use it as described in the article mentioned above. I hope this was clear and helpful.

April 6, 2009

Configuring Text Fields (Part 2)

In Part 1 of this article, I described the various properties used to modify the "look and feel" of text fields. Most of these properties deal with the visual aspects, such as font and background color. They also handle various techniques for clearing out the text in a text field. In this second part, I will focus on the virtual keyboard and the behaviors of changing your entered text (due to spelling or capitalization errors). It is likely that most people will be interested in changing some of the default behaviors. As revolutionary as the iPhone virtual keyboard is, some of the things it does can be quite annoying for some applications. At the same time, some of the customizations you can make really help the usability of your application, so it is best to understand all your options in order to make the best user experience possible. These are additional properties of the UITextField object you can change: autocapitalizationType
Setting this property determines when characters are auotmatically capitalized. The allowed enumerations are:
  • UITextAutocapitalizationTypeNone
  • UITextAutocapitalizationTypeWords
  • UITextAutocapitalizationTypeSentences
  • UITextAutocapitalizationTypeAllCharacters
None will not autocapitalize anything. Words will capitalize the first character of every word, whereas Sentences will capitalize the first character of every sentence. AllCharacters will capitalize every character. The default is None.
autocorrectionType
The autocorrection mechanism on the iPhone allows you to type "fast" and it will correct most of your mistakes. I find this works well, but in some cases I don't want this behavior. Luckily you can disable it. This property can be set to one of these values:
  • UITextAutocorrectionTypeDefault
  • UITextAutocorrectionTypeNo
  • UITextAutocorrectionTypeYes
The Default defers to what the documentation refers to what the "script system" supports, with no explanation of what a script system is. I assume this indicates in what context the keyboard is used. The UITextField is not the only user of the keyboard. By setting autocorrectionType to Default, you will get the auto-correction behavior *if* your current script system supports it. For the purposes of UITextField, it does support auto-correction. Setting this property to No will disable auto-correction. The default is Default, which for UITextField views, means auto-correction is enabled.
enablesReturnKeyAutomatically
By default, the return key on the keyboard is enabled. However, you can change this to be disabled *until* some text has been entered in the text field. I'm not sure what use this is for text fields, but you might have a need. If you set this property to YES, then you will have this keyboard when you have no text entered:
The return key is disabled, but as soon as you entered some text, the keyboard changes to this:
keyboardAppearance
There are two types of keyboard appearances supported: one has the blue-gray background like the one above, and the other has a black background, like you see on alerts. The two enums this property supports are:
  • UIKeyboardAppearanceDefault
  • UIKeyboardAppearanceAlert
If you choose the alert appearance, the keyboard looks like this: As far as I can tell, there is no other difference in looks or behavior with this keyboard
keyboardType
Depending on what kind of input you are gathering in your text field, you can have different keyboards that are optimized for that type of input. The keyboardType property can be set to one of the following enums. I also show what each keyboard looks like:
UIKeyboardTypeDefault:

UIKeyboardTypeASCIICapable:

The default looks the same as this. My guess is the default can change depending on context, whereas the ASCIICapable one always look like this.

UIKeyboardTypeNumbersAndPunctuation:

This is your standard numbers and punctuation keyboard.

UIKeyboardTypeURL:
   

This keyboard is handy if you are typing in a URL. Also note that if you tap and hold on the .com button, you'll get a little pop-up window that will allow you to choose .edu, .net, .org, etc. Here is what that looks like:

UIKeyboardTypeNumberPad:

This is useful if all you need is to enter numbers.

UIKeyboardTypePhonePad:

This is similar to the NumberPad, but this one resembles a phone keypad, with the letters under the numbers and the "+*#" button.
UIKeyboardTypeNamePhonePad:

This looks like your standard ASCII keypad, but the difference is when you hit the digits button in the lower left, you get a phone-specific keypad:

UIKeyboardTypeEmailAddress:

These are slightly optimized for entering email addresses.
returnKeyType
Just the return key can be changed with different labels. Like the keyboardType property, this allows you to customize the keyboard to fit your scenario. However, many of these are very application specific and may not have much use for text fields: UIReturnKeyDefault UIReturnKeyGo UIReturnKeyGoogle UIReturnKeyJoin UIReturnKeyNext UIReturnKeyRoute UIReturnKeySearch UIReturnKeySend UIReturnKeyYahoo UIReturnKeyDone UIReturnKeyEmergencyCall I won't show what all these keyboards look like. The only difference is the label on the return key. The useful ones for most text fields would be Next and Done. When the return key is tapped, no matter what text is on it, the text field will call the textFieldShouldReturn: method on its delegate. You can use this callback as a means to do something when the return is hit. One common trick is have textFieldShouldReturn: resign the first responder, or in normal English: give up the focus, which in turn hides the keyboard.
secureTextEntry
If your text field is for entering passwords or other sensitive information, you can get the password-like entering behavior by setting this property to YES. When this is YES, only the last character entered will display in the text field; all others are replaced with a dot. Here is what is looks like:
There you have all the properties you can change to modify the keyboard and auto-correction behaviors of your text fields. Instead of accepting the defaults, you can customize your text fields and make them more user-friendly and efficient.

April 2, 2009

Configuring Text Fields (Part 1)

For something as seemingly simple as the UITextField view, it is a fairly rich control. Because of the limitations of the iPhone interface, the text field can be customized in many different ways to make it easier to use. This two-part article will attempt to shed some light on all the options used to configure the text field, as they are lightly documented in the SDK documentation. Even the iPhone "Bible" (Beginning iPhone Development) only gives it a cursory mention. Most of the configurations on a UITextField object is done through properties. In Part 1, I describe the properties used for modifying the behavior and look of text fields. In the follow-up Part 2, I describe the properties used to configure the virtual keyboard that is associated with each text field. So on to the properties:
adjustsFontSizeToFitWidth minimumFontSize
As you enter text in a text field and you reach the end of the field, it will scroll left so that what you are typing will stay visible. The following image shows what this looks like when entering a long input:
The text at the beginning of the text field has scrolled to the left so that it is no longer visible. If you want to support long input but not scroll, you can set the adjustsFontSizeToFitWidth property to YES so that the font will reduce in size in order for the entire input to remain visible. Here is what a long line looks like with this option set to YES:
See how the font got smaller? Of course, the font won't get infinitely smaller, else you wouldn't be able to read it. Once the font size reaches its minimum size, it begins to scroll. The text in the image above was the smallest it would get. The next character I typed started the field scrolling. While this technique helps a little, it really doesn't offer much more vertical space. According to the documentation, there is another property called minimumFontSize that allows you to specify what the minimum is before the shrinking stops and the scrolling starts. By default, this value is 0.0. But no matter how I changed this property, the font shrinking behavior never changed. This may be a limitation in the iPhone Simulator, as I did not try this on a real iPhone.
background disabledBackground
The background property allows you to provide a background image for the text field. The image will be scaled to fit the field. Here is an example of a text field with a custom background:
Although it appears the image does not fill up the entire text field, be aware that my text field example lives inside a table cell, which has its own border around it. The disabledBackground property is similar, but is used when the text field is disabled. This property is ignored unless the background property is also set.
backgroundColor
You can change the background color from the default white to any color. If you have a background image specified, it will override your backgroundColor settings. Here is a text field with a red background:
borderStyle
If you have no background image, you can change the border style of the text field. This property can be set to one of four possible enums. The following image shows what the border style looks like with the enum name entered in the text field:
The default is UITextBorderStyleNone.
clearButtonMode
This property allows you to put a little "x" button on the right to quickly clear out the contents of the text field. It looks like this:
This property can be set to one of several enumerations, which determine when this clear button is shown:
  • UITextFieldViewModeNever
  • UITextFieldViewModeWhileEditing
  • UITextFieldViewModeUnlessEditing
  • UITextFieldViewModeAlways
The WhileEditing enum will only show the button while you are editing that particular text field. The UnlessEditing enum will do the opposite: it will show the button when you are not editing the field, and hide it when you are editing. In this context, editing means that the field has the focus. Never and Always should be obvious. The default value is UITextFieldViewModeNever.
clearsOnBeginEditing
Here is another property for controlling clearing the contents of a text field. When this is set to YES, the field will remove whatever is there when it gets the focus. By default, this is set to NO.
enabled
By default, your text fields are enabled, meaning they can accept input. If for some reason you wanted to disable a text field (making it read-only), then set the enabled property to NO. Disabling a text field doesn't change its appearance. However, if you are setting a custom background via the background property, and have also set a disabledBackground property, then this latter property will go in effect if the text field is disabled.
font
This changes the font of the text inside a text field. It takes a UIFont object. Here is an example of setting the font to [UIFont italicSystemFontOfSize:16.0]:
The default font is 12-point Helvetica plain.
leftView leftViewMode
These two properties allow you to put a view, like an image, on the left side of the text field. Any image will be scaled to fit the space. Here is an example of putting a little magnifying glass icon there:
Just setting leftView to an image is not enough, though. You also have to set the mode. The modes are the same as for clearButtonMode. The default is UITextFieldViewModeNever. This property accepts any view, as the name implies, so you could even put a button here (this is how the clearButton does it).
placeholder
When this is set to a string, it displays that text when there is no text in the text field. As soon as you start entering text, the placeholder disappears. It simply gives you a hint as to what is supposed to be entered in the text field. Here is what it looks like:
Notice how the font color is not so dark. By default, the placeholder is empty.
rightView rightViewMode
These two properties work the same as leftView and leftViewMode, except it deals with the space on the right hand side of the text field. Here is what that looks like:
text
This property contains the text that is displayed on the text field. It is a read/write property, so you can pre-populate the text in a text field before showing it. And, of course, you can read the text so you know what the user entered.
textAlignment
The allows you to align the text in the text field horizontally. This setting also applies to any placeholder text. The allowed values are:
  • UITextAlignmentLeft
  • UITextAlignmentCenter
  • UITextAlignmentRight
The default is UITextAlignmentLeft. Here are three text fields, each with a different textAlignment setting:
textColor
You can change the text color from the default black. Here is an example of setting the text color to [UIColor redColor]:
There you have most of the properties that affect the behavior and look of a text field. In the next part, I will cover the properties that have to do with the virtual keyboard.

March 11, 2009

Trapping the UINavigationBar Back Button (part 2)

After posting the first part of this article, it was pointed out to me that there is an alternative solution: handle the viewWillDisappear message. This is certainly a more elegant solution, but it does have its drawbacks, which is why I did not use it in the first place. However, it is a valid solution in some cases and can be of use to some people, so I will describe it here. There is a set of messages that get sent to your UIViewController subclass during various events:
- (void)viewWillDisappear:(BOOL)animated - (void)viewDidDisappear:(BOOL)animated - (void)viewWillAppear:(BOOL)animated - (void)viewDidAppear:(BOOL)animated
These are called for the event described in the method name. For example, the viewWillDisappear is called just before the view disappears. Now for my situation, I needed to trap the event of my controller being popped off. The problem with using viewWillDisappear (or viewDidDisappear) is that it is called in different scenarios. When a controller is pushed on top of yours, the viewWillDisappear method is called. And when your controller is popped off, the method is called again. The problem for me is the event is too general. Your view is disappearing, but the reason why it's disappearing is not provided. In my situation, my controller can push other controllers, but I only wanted to know when I was popped off, not pushed on top of. In order to differentiate between the two scenarios, I need additional intelligence in my code to determine which situation I am in. Basically, whenever I am about to push on a new controller, I have to set a flag to ignore the next viewWillDisappear message. Then I need to handle the viewWillAppear message to clear this flag. Not a big deal, but seems like a hassle to handle something fairly basic. Here is the framework needed to support this:
// ---------------------------------------------------------
//
// YourController.h
@interface YourController : UIViewController
{
 BOOL ignoreDisappear;
}
@end

// ---------------------------------------------------------
//
// YourController.m

// this helper method will push a new controller onto the navigation stack
- (void) myPushController:(UIViewController*) newController
{
  // first, set our flag to ignore the next viewWillDisappear message
  ignoreDisappear = YES;

  // now do the push
  [self.navigationController pushViewController:newController animate:YES];
  [newController release];
}

- (void) viewWillDisappear:(BOOL) animate
{
  // do we care about this event?
  if(ignoreDisappear == NO) {
     // this is your back button handle logic
     //...
  }

  [super viewWillDisappear:animate];
}

- (void) viewWillAppear:(BOOL) animate
{
  // clear the flag
  ignoreDisappear = NO;

  [super viewWillAppear:animate];
}
But that was my situation. If your controller is at the end of the UINavigationBar hiearchy, meaning you cannot have another controller pushed onto yours, then you are safe assuming that the viewWillDisappear is being called for the scenario where your controller is being popped off. Take caution, though. Should you later change the behavior of your code to allow additional controllers to be pushed, your viewWillDisappear will get invoked in what may be an inappropriate time. One other thing I should mention. There is this warning in the Apple reference in each of the four methods listed above:
Warning: If the view belonging to a view controller is added to a view hierarchy directly, the view controller will not receive this message. If you insert or add a view to the view hierarchy, and it has a view controller, you should send the associated view controller this message directly. Failing to send the view controller this message will prevent any associated animation from being displayed.
I'm not real clear on what the phrase "added to a view hierarchy directly" means, but there are numerous reported problems related to this. I don't have these problems myself, but they are reported here and here.

March 7, 2009

Trapping the UINavigationBar Back Button

I recently had a need to trap the back button tap on the navigation bar in order to do something before popping to the previous controller. However, I soon discovered that this was not possible. The standard back button is a "special" button that developers cannot override, at least not with the current public SDK.
The only thing you can change on the standard back button is the text. By default, this is the title of the controller. You can change it with code like this:
[self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"New Title" style:UIBarButtonItemStyleBordered target:nil action:nil]; 
The important but subtle thing about a controller's backBarButtonItem property is that it is not the back button displayed when that controller is on top; it is for other controllers that are pushed on top of it. In other words, if you have a "root" controller whose title is something very long, you set its backBarButtonItem title to something shorter. Then when other controllers are pushed on the stack, they will use this back button item as their back button. This is unintuitive at first glance, but makes sense when you think about it: you can redefine the back button title once, and all other controllers that are pushed on top will use this back button.
Even though there are four parameters to the initializer, only the title is used for this "special" back button. The style specified seems to be ignored, as are the target and action parameters. Because of this, you cannot insert your own event handler for the back button, which is what I needed to do.
After some digging and research, I have a solution. It is not a pretty solution, but it's good enough.
The navigationItem property of a UIViewController has another property called leftBarButtonItem. This, like the backBarButtonItem, is a UIBarButtonItem object. If you set this to something other than nil, then this new button will replace the standard back button item. So now, I can get my custom back button event handler to work, because this one honors the style and the target/action parameters:
- (void) viewDidLoad
{
   // change the back button and add an event handler
   self.navigationItem.leftBarButtonItem =
   [[UIBarButtonItem alloc] initWithTitle:@"Pages"
                                    style:UIBarButtonItemStyleBordered
                                   target:self
                                   action:@selector(handleBack:)];
}

- (void) handleBack:(id)sender
{
    // do your custom handler code here

    // make sure you do this!
   // pop the controller
    [self.navigationController popViewControllerAnimated:YES];
}

If you still want this button to behave like a back button, it is important to put the popViewControllerAnimated: call in your handler. I should also say that this leftBarButtonItem should be set on the controller who will actually show the back button, unlike the backBarButtonItem. Now the reason I stated this wasn't a perfect solution is the following: The "special" back button looks different. Here is what the standard backBarButtonItem looks like:
Yet, the leftBarButtonItem looks like this:
I have been unable to force my custom back button to have the different shape. This is another indication that this back button object is treated specially that is not exposed in the SDK. Other than this little aesthetic discrepency, this approach met my needs. Hopefully it will meet yours as well.

March 6, 2009

A General Purpose Table Form Editor

Note: You may have come here from another link. Be aware that I have updated this package and can read about it here. Sometimes your iPhone applications need the ability to enter or edit data on a form. For example, here is a screen from the contacts app when adding a new contact:
If you have ever coded up the UITableViewController class to do this sort of the form editing, you know that this coding task is very tedious and time-consuming. Wouldn't it be nice if there was a general purpose, reusable class for doing this? Here I present such a class.

Features

The TableFormEditor class is intended to be used within a UINavigationController. In other words, you allocate a TableFormEditor instance, configure it, then push it on top of the navigation stack. The TableFormEditor will create a grouped table and the first section will contain rows with UITextField objects to display and allow editing of data. These rows can optionally contain labels that identify what each row represents, or you can have no labels as in the above example. Optionally, you can configure a delete button to appear at the bottom of the form. This is intended to allow the user to delete the record. The text on the button is configurable. The action of the delete button will ask for confirmation before continuing.

Basic Usage

Here is how the TableFormEditor works:
  1. You allocate and initialize a TableFormEditor object.
  2. You configure various other aspects of the object, such a your fields names and what your data is.
  3. Then you push the object onto the navigation controller stack. At this point, the TableFormEditor is in control.
  4. You can bounce around the fields and edit to your heart's content. When you are done, you hit the "save" button. This will pop the controller off the navigation stack and send a message to the delegate you provide, passing the new data. It is up to your delegate to do something with this data.
  5. If you decided to hit "cancel" instead, the controller is popped off the navigation stack and a message is sent to the delegate.
  6. If you have a delete button enabled, and this button was selected, then the controller is popped off the stack and a different message is sent to the delegate.
Each of these steps is discussed in greater detail below.

Initialize

There are two schools of thought concerning initializers. Some like to force the user to pass all required parameters, often resulting in very long initializer statements. The other side prefers simple initializers, and the remainder of the parameters can be set as properties. The benefit of the former is that there will be no forgetting to set a required parameter. However this approach is not very flexible; any new additions or changes to the class often require the initializer to be changed. Very specialized initializers create fragile code. Therefore I have opted for the approach of simple initializers and setting properties. True, some properties must be set, otherwise the resulting code will be next to useless. However, I will document clearly which properties are mandatory and which are optional. There are two modes the TableFormEditor can be in: edit mode or add mode. In edit mode, you are editing/viewing existing data. In add mode you are adding a new record. What mode you're using has subtle differences in the default behavior. For instance, in add mode there isn't a delete button displayed, since there is no record yet to delete. To initialize for add mode, use the initForAdd: method:
TableFormEditor* add = [[TableFormEditor alloc] initForAdd:self];
All you provide is the delegate, typically self. The delegate must conform to the protocol TableFormEditorDelegate:
@protocol TableFormEditorDelegate
@optional
- (void) formDidCancel;
- (void) formDidSave:(NSMutableDictionary*) newData;
- (void) formDidDelete;
@end
All these methods are optional, but obviously without the formDidSave: you don't have very functional software. To initialize for edit more, use the initForEdit: method.
TableFormEditor* edit = [[TableFormEditor alloc] initForEdit:self];
Just as with the add mode, you pass in a delegate to handle the same protocol.

Configure

Now we'll discuss all the various properties you can and must set to configure the form editor. Note that even though some of these are labelled as "required", that doesn't mean your program will crash without them. But you may not see very interesting results.

Required

NSArray* fieldNames
This is a list of NSString objects which are the field names. Each field equates to a row in the form. This list serves several purposes:
  1. It determines how many rows to create in the table,
  2. It provides the labels to preface each row with,
  3. It provides the order to present the fields in the form, and
  4. It provides the key names to be used in the data dictionary.
Note that the data you provide in the data dictionary (described next) must use keys that match these names.
NSMutableDictionary* fieldData
This is the data. Depending on the mode the object was initialized with, this data has different purposes. If initialized in edit mode, then this data represents the current data. The keys are the fieldNames given above. The values are the data. Both of these are assumed to be NSString objects. If the form editor was initialized in add mode, then this data represents the placeholder text to put in each row. The example above shows placeholder text on each row. This text gives the user a hint what's to go in that field, and it disappears as soon as you start entering text. If you don't want placeholders, don't set the data. This dictionary is copied (deep copy), because TableFormEditor may modify the values.

Optional

id <TableFormEditorDelegate> delegate
This is the delegate to handle the callbacks when the user wants to exit the form. This is set up in the initializer, but if you ever need to change it, you can via this property.
BOOL showLabels
When the form rows are displayed, by default each row has the field name displayed on the left as a label. This may be overkill, especially if you use placeholders, so you can turn off the labels by setting this property to NO.
BOOL allowDelete
In edit mode, by default, this is set to YES. When YES, a delete button will be displayed after the form. Set to NO if you don't want to see the delete button. If you are in add mode, and for some reason want the delete button, you will have to force it by setting the property to YES.
NSString* deleteButtonLabel
If a delete button is displayed, this is the text used. If unset, it will use "Delete".
NSString* saveButtonLabel
The TableFormEditor puts a button in the right position of the nav bar. This button is for saving your changes and exiting the form. By default, the button text is "Save", but this can be modified with this property.
NSString* cancelButtonLabel
The TableFormEditor also puts a button in the left position of the nav bar. This is for canceling any edits made and exit the form. By default, the button text is "Cancel". Modify with this property.
NSInteger firstFocusRow
By default, the form editor will place the focus (first responder) on the first row (row 0). This action brings up the keyboard automatically. If you wish to have the focus placed somewhere else first, set this property to the row you want. If you don't want any row given focus (and thus no keyboard will come up automatically), then set this property to -1.
NSString* title
This is the title string placed in the middle of the nav bar. Default value is blank.

Future

In future releases of TableFormEditor, I will be adding more configuration options, like options to configure the keyboard and how the text fields behave.

Download

The source code to the TableFormEditor class can be freely downloaded here: version 1.0 . This software is covered under the MIT License.

February 26, 2009

Radio-button behavior for table rows

One thing missing from the iPhones UI kit is a plain old radio button. There are some controls that kind of give you the exclusive selection behavior, like a tab bar or a segment control, but there is nothing like a typical RadioButton widget. The closest you can probably get using the standard UIKit framework is a table. Tables have rudimentary support for radio button behavior. When using the standard UITableCell class, it has provisions for setting the accessory type of a row to a checkmark. You have probably seen this technique in the calendar app while editing an event alert. Your choices are presented like this:
Unfortunately, this is pure visual asthetics; you still have to manage the exclusive behavior in your code. The document "Table View Programming Guide for iPhone OS " has a good example of how to implement both exclusive and inclusive selections in chapter 7 that makes use of the checkmark accessory. I won't repeat that code here, but in a nutshell, this example code handles the message tableView:didSelectRowAtIndexPath:, and what it does (for the exclusive selection behavior) is find the previously selected cell, clear it's accessory checkmark, and put the checkmark on the current cell. It's a little cumbersome, but it works. This is all fine, but what if you didn't want to use the checkmark accessory? I have an application where I need to use the disclosure button accessory, so I needed to implement the checkmark somewhere else. The solution I chose was to create my own checkmark icon and place that in the image portion of the standard cell, over on the left hand side of the cell. I quickly discovered, though, that you can't use the same technique as in the above document. If you change the image of a cell, the changes will not be seen. For some reason, changing the accessory type of a cell is rendered immediately, but not so for the image. The only way I could get the image change to be reflected is to call reloadData: on the table view. After I did that, everything worked. However, I took this opportunity to clean up the clunky code from the example. Instead of finding my old cell, all I do in tableView:didSelectRowAtIndexPath: is set the current active row in our data and call reloadData:. The real work was moved over to the table:cellForRowAtIndexPath: method, which sets the image for each cell according to the active row. First we must handle the row selection. The instance variable activeRow is simply an integer of the currently selected row.
- (void)
tableView:(UITableView *) aTableView didSelectRowAtIndexPath:(NSIndexPath *) indexPath
{
 [aTableView deselectRowAtIndexPath:indexPath animated:NO];

 if(indexPath.row != activeRow) {
    // reset the active account
    activeRow = indexPath.row;

    // tell the table to rebuild itself
    [aTableView reloadData];
 }
}
Next we handle the actual image setting:
- (UITableViewCell *)
tableView:(UITableView *)aTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
 UITableViewCell *cell = [aTableView dequeueReusableCellWithIdentifier:@"Cell"];

 if (cell == nil) {
    cell = [[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:@"Cell"];

    // set up the cell properties that are the same for all cells on this table
    cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;
 }

 // get our text for the row
 cell.text = [self.data forRow:indexPath.row];

 if(indexPath.row == activeRow) {
    cell.image = checkedImage;
 }
 else {
    cell.image = uncheckedImage;
 }

 return cell;
}
After we get a cell to work with, I compare the row with the active row. If it is a match, I use the checked image. If not I use the unchecked image. Here is the final output:

February 23, 2009

Tab Bar Icons (Part 1)

Tab bars are for toggling between application views. This is a common controller on the screen-constrained iPhone. Not to be confused with toolbars, tab bars are the black bars at the bottom of the screen with gray unselected icons and one blue selected icon. You see tab bars in the phone and ipod and YouTube apps, among others. Here is an example, from the ipod app:
When you are creating a tab bar for your own application, you face the problem of providing the icons. You have several options here. Your first option is to use one of Apple's system icons. Like the navigation bar, you have access to some of Apple's system icons, and they even encourage you to use them. Unfortunately, there are a couple caveats with using these. First, the list is pretty skimpy. Second, the icons include the text as well. You cannot set the image and the text separately; it's both or nothing. Here is what is offered:
icon text enum
More UITabBarSystemItemMore
Favorites UITabBarSystemItemFavorites
Featured UITabBarSystemItemFeatured
Top 25 UITabBarSystemItemTopRated
Recent UITabBarSystemItemRecents
Contacts UITabBarSystemItemContacts
History UITabBarSystemItemHistory
Bookmarks UITabBarSystemItemBookmarks
Search UITabBarSystemItemSearch
Updates UITabBarSystemItemDownloads
Most Recent UITabBarSystemItemMostRecent
Most Viewed UITabBarSystemItemMostViewed
If you can make use of them, you access them via an enum for each defined in the UITabBarItem class. Here is how you would programatically set your tab bar image to the magnifying glass:
self.tabBarItem = [[UITabBarItem alloc] initWithTabBarSystemItem:UITabBarSystemItemSearch tag:0];
You execute this in your UIViewController subclass that you are putting into the tab bar. This is best done in your view controller's initializer method:
- (id) init
{
self.title = "search";
self.tabBarItem = [[UITabBarItem alloc] initWithTabBarSystemItem:UITabBarSystemItemSearch tag:0];
return self;
}  
If the system icons don't meet your needs, some people have suggested doing a screen capture of an app that has an icon you like. Ignoring the legalities of this, this may not give you good results because Cocoa-Touch has already applied affects to the icons. Your next option would be to search for some free or pay-for icons. I noticed many freelance graphics designers putting their shingle up and offering their services. I didn't look long, but I did not find any suitable, free icons for iPhone tab bars. This leaves you one last option: create your own. So the first questions that come up are:
  1. what format?
  2. what size?
  3. what colors?
After researching these questions, I found some conflicting answers and lots of confusion among the iPhone developer community. I will try to summarize and disperse the clouds. For format, this is pretty well agreed upon to be PNG, which is the image format of choice on the iPhone. You're not limited to this format, but PNG is the recommended format and gives best performance. The background should be transparent, else your icon will be a plain square. The size issue is a bit more contentious. I saw reports of sizes ranging from 20 x 20 all the way up to 57 x 57. Most people don't differentiate between pixel or point sizes, and they are different. Here is what Apple says in the reference documentation for the UITabBarItem:
If this image is too large to fit on the tab bar, it is scaled to fit. The size of an [sic] tab bar image is typically 30 x 30 points.
Note it doesn't say what the max size is before it gets scaled. One poster thought the max size was around 48 x 32 before scaling happens. But if Apple doesn't publish this information explicitly, I would hesitate to use it. You could explore yourself to find out what the max is, but obviously Apple would like to retain some latitude in this area to possibly change it in future firmware releases. I believe 30 x 30 is a safe size that will give you the best look. There also seems to be a great deal of confusion regarding colors. From my research, colors are basically ignored. Here is what the Apple document "View Controller Programming" has to say about it:
The unselected and selected images displayed by the tab bar are derived from the images that you set. The alpha values in the source image are used to create the other images—opaque values are ignored.
In other words, Cocoa Touch will create new images from your source images, using alpha values only. Alpha, in the PNG world, is the degree of a pixel's opacity. Maximum alpha values denote a completely opaque pixels. In the example below this quoted text, they have an example source image and the resulting unselected and selected icons. Color is irrelevant. In part 2 of this article, I will discuss various tools used to compose icons, specifically how to create tab bar icons.