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.

7 comments:

  1. Super, but how to use in a scrolllView above the keyboard (I want to edit about 20 cells) ?

    ReplyDelete
  2. I haven't tried this code on a real iphone; only the simulator. I filled up the window with 12 fields and was able to scroll to see the other fields. Can't you just scroll with your finger?

    ReplyDelete
  3. Looks like a great control and I'd love to play around with it. I'm very new to iPhone development, so could you provide an example of how to link to it from one view to another? It is as simple as creating a new view and popping it onto the stack?

    ReplyDelete
  4. Yes, the TableFormEditor is a subclass of UITableViewController. So after you create it, simply push it on the navigation controller's stack.

    I will try to provide an example soon.

    ReplyDelete
  5. Excellent. Thanks for the tip.

    I'll keep an eye on this blog entry for your example too.

    ReplyDelete
  6. Could you explain what this line of copy means:
    The delegate must conform to the protocol TableFormEditorDelegate.

    Within my code, I'm using this line of code for add:
    TableFormEditor* editor = [[TableFormEditor alloc] initForAdd:self];

    When I compile the the page, I'm receiving a warning stating that testViewController does not implement the 'TableFormEditorDelegate' protocol. What does this mean? I have included TableFormEditor.m within my view controller .m file.

    Thanks,
    Mike.

    ReplyDelete
  7. The TableFormEditorDelegate defines the protocol that the delegate you provide to the TableFormEditor class must adhere to. This protocol is defined in the TableFormEditor.h file and specifies the callbacks that the table form editor will make when the user hits the save button, or delete button, or cancel button.

    Typically you import the header file, not the .m file.

    You can get a more complete example in my latest post; I provide a complete XCode project that shows how to use this class.

    ReplyDelete