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 9, 2009

Putting a Button in a Table

Sometime you may need to add a button to the contents of a UITableViewController. While you have the ability to put buttons in the navigation bar or in a toolbar at the bottom, it is sometimes best to simply put the button "inline" near your data. Sure, you could simply put a UIButton control right in a UITableCell object, but I have found the results of that not very attractive, especially in grouped tables. So what's the best way? I like to use the Apple applications as examples of good design. There are a couple Apple applications that put UIButtons in UITableViewControllers. Take a look at these examples. This one is from the mail settings, where there is a "Delete Account" button at the bottom of the table:
And this one is when you are editing a contact, where there is another "Delete" button at the bottom:
Although they are the same size of a single cell section, you can tell these are not normal rows. The giveaways are the rounded corners that don't quite match those of the neighboring rows. My guess is these are custom buttons, which stretch a background image to fit a frame that's the same size of a cell. But using UIButton controls is not the only way to get button behavior. You can simulate a button with just a basic grouped table cell; no UIButton at all. This is what I call cell-based buttons, for lack of a better term. I found a couple Apple applications that appear to use this technique. For example, here is the the settings from Safari:
That section of three rows in the middle contain cells where each operates as a button. The phone settings also uses this, with the "Change Voicemail Password" cell in the middle. There are probably other ways to get button functionality into your tables, but these two seem to be the most common. This article will explain the advantages and disadvantages of each approach, and I'll provide some code snippets that shows how to do them.

Cell-Based Buttons

Simulating a button in a cell has one big advantage: it's super easy to do. If you don't care about it looking fancy, or want to bunch up several "buttons" in one section, as the one above for editing a contact, then a cell-based button is a good option. For simple black on white text, all you have to do is set the cell.text property to your button text. Since the visual aspects of a table cell can be customized quite extensively, you really have lots of flexibility here. However, once you go beyond the standard UITableCell object, the complexity of your code increases. I believe that once you have gone beyond the basic black on white text, you might as well go with a real button.

Implementing

How a cell-based button works is that your code simply traps the tableView:didSelectRowAtIndexPath: message. You associate the section and row with which "button" was hit and perform whatever action that button is supposed to do. For example, from the image above, you can associate that block of buttons with section #1, and row #0 is the "Clear History" button, row #1 is "Clear Cookies", etc. One thing to remember is that the action of touching a cell automatically highlights the cell. Before you do anything else, you should turn off that highlight. Here is some example code of how we might implement the above section of three buttons. There are two aspects here: setting up the buttons for display, and handling the actions when the buttons are selected. Setting up for display. All of this is fairly boilerplate code for the tableView:cellForRow: message. The big difference here is that we are not fetching the row contents from a database, but hard-coding the button title. This can easily be internationalized as well.
- (UITableViewCell *)
tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{

   static NSString *CellIdentifier = @"Cell";

   UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
   if (cell == nil) {
       cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
   }

   // are we setting up our button section?
   if(indexPath.section == 1) {
       switch(indexPath.row) {
           case 0: cell.text = @"Clear History"; break;
           case 1: cell.text =@"Clear Cookies"; break;
           case 2: cell.text = @"Clear Cache"; break;
       }
   }
   else {
       // do whatever for the other sections
   }

   return cell;
}
For cell-based buttons, you want to avoid using the accessory view, like putting in a detailed disclosure button or the checkmark. Having these items on a "button" will be confusing. Handling actions. All you do here is figure out which "button" was hit and do whatever it is you need to do. Also notice the code to turn the highlight off.
- (void)
tableView:(UITableView *) aTableView didSelectRowAtIndexPath:(NSIndexPath *) indexPath
{
   // remove the row highlight
   [aTableView deselectRowAtIndexPath:indexPath animated:YES];

   // Is this section our "button" section?
   if(indexPath.section == 1) {
       switch(indexPath.row) {
           case 0: [self doClearHistory]; break;
           case 1: [self doClearCookies]; break;
           case 2: [self doClearCache]; break;
       }

       return;
   }
   else {
       // do other stuff...
   }
}

Real Buttons

Real buttons offer all the advantages you get with a real button. For one thing, the size of the button can be fully customizable. This allows you to have two buttons side by side, as in this example from showing detailed info of a contact:
You can't have two table cells side by side like that, so these have to be buttons. You'll note that these rounded corners match the rounded corners of the table cells. I think these buttons use the normal RoundedRect button, as opposed to custom buttons. Custom buttons are mostly needed when you want a background color other than white. But if all you need are plain white backgrounds, you can use the RoundedRect buttons. Which leads to the other advantage of using real buttons: better control over background colors. Setting a background color in a grouped table cell is difficult. Just simply setting the background property to a color is not enough, as the rounded corners do not get the color. The only solution I've seen to this problem is a complicated bit of code to color in the curved corners (as demonstrated here). You also don't get the cool gradients as you see in the two red buttons on the top two examples.
There are probably other advantages to using real buttons, but to me these are enough to force me to use buttons sometimes.

Implementing

So how are UIButtons integrated into a UITableView? Again, there may be several ways, but I find the easiest is to sneak the buttons into a section header. Each section can have a header view. In this view is where you put your UIButton objects. The section itself has no data rows, only a header. So what I do is accommodate for one extra section in my table, but return 0 for the number of rows.
- (NSInteger)
numberOfSectionsInTableView:(UITableView *)aTableView
{
   // return the number of sections the data is organized in, plus 1 for the button
   return 2 + 1;
}
Assuming you want your button at the end of the table, you would use code like this to return the number of rows:
- (NSInteger)
tableView:(UITableView*) aTableView numberOfRowsInSection:(NSInteger) sectionNum
{
   if(sectionNum == 3) {
       // this is our button section
       return 0;
   }
   else {
       // do whatever, according to the data
   }
}
Now we have to create our buttons. This is done in the tableView:viewForHeaderInSection:
- (UIView *)
tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)sectionNum
{
   if(sectionNum == 2) {
       // create the parent view that will hold 1 or more buttons
       UIView* v = [[UIView alloc] initWithFrame:CGRectMake(10.0, 0.0, 300.0, 44.0)];
 
       // create the button object
       UIButton* b = [UIButton buttonWithType:UIButtonTypeCustom];
       [b setBackgroundImage:[[UIImage imageNamed:@"redbutton.png"] stretchableImageWithLeftCapWidth:12.0 topCapHeight:0.0] forState:UIControlStateNormal];
       [b setBackgroundImage:[[UIImage imageNamed:@"bluebutton.png"] stretchableImageWithLeftCapWidth:12.0 topCapHeight:0.0] forState:UIControlStateHighlighted];

       b.frame = CGRectMake(10.0, 0.0, 300.0, 44.0);
       [b setTitle:@"Button Title" forState:UIControlStateNormal];

       // give it a tag in case you need it later
       b.tag = 1;

       // this sets up the callback for when the user hits the button
       [b addTarget:self action:@selector(action:) forControlEvents:UIControlEventTouchUpInside];
     
       // add the button to the parent view
       [v addSubview:b];

       return [v autorelease];
   }
   else {
       // stuff for other sections
   }
}

The values for the frames are specific to portrait mode; landscape mode will need some modifications. To add more buttons in this view, just adjust the y parameter in the frame for the button. I typically add 46.0 from the last one. This code makes use of two button images:

I got these from user "hakimny" from this thread. Creating these aren't hard. (I have posted an article on how to create simple background images like this in Inkscape.)
If you want to create a button with a white background, then you don't need to create a custom button. You can create a RoundedRect button that will look more like the surrounding cells, like you saw above. In this case, your button creation is simply:
b = [UIButton buttonWithType:UIButtonTypeRoundedRect]; 
You don't need to set the background images. If you want to create two side by side buttons, all you have to do is monkey around with the frames of the buttons so that they are side by side instead of on top of each other. There is one gotcha I have discovered using this mechanism, and I think it is an SDK bug. If your table takes up more than one screen and you have to scroll to see your button, some of the button may remain hidden under the tab bar or tool bar. You won't be able to scroll the entire header view into view. This is especially noticeable if you have 2 or more buttons stacked on top of each other. I think Cocoa Touch is confused by there being no data rows, or maybe it can't quite figure out how high the header view is. So the way around it is to tell how high your view is. Once you do this, the view is no longer partially hidden:
- (CGFloat)
tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)sectionNum
{
   return 46.0;
}

Of course, if you have more buttons in this section, you would return the appropriate height. There you have a quick tutorial on putting buttons in a table. How you choose which mechanism to use is up to you. My own guidelines are as follows:
  1. If I want a simple black on white button: use a cell-based button
  2. If I need a colored button: use a real button
  3. If I want multiple buttons per row: use real buttons

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.