One thing that I see quite often is UITableViewCells that have a UIControl (like a UIButton or UISwitch) on them. Let’s learn how to make UIButtons and UIControls in UITableViewCell simple.

In order to communicate which particular cell’s button was tapped, a programmer might set the tag property of the button to the row it appears on:

- (UITableViewCell *)tableView:(UITableViewCell *)tableView
         cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    ButtonCell *c = ...;
    [[c button] setTag:[indexPath row]];
    [[c button] addTarget:self action:@selector(doStuff:) forControlEvent:UIControlEventTouchUpInside];

    return c;
}

- (void)doStuff:(id)sender
{
    int row = [sender tag];

    // Do stuff with row
}

Of course, in a sectioned table view, there needs to be an additional index, so I’ve seen some programmers use a bigger number to determine the tag:

[[c button] setTag:[indexPath section] * 1000 + [indexPath row]];

- (void)doStuff:(id)sender
{
    int section = [sender tag] / 1000;
    int row = [sender tag] % 1000;
}

This is a really brittle solution because it requires recalculation when you change the organization of the table view and it is very prone to error.

Therefore, I present you with STKTableViewCell.

STKTableViewCell simplifies using target-action pairs in UITableViewCells. It also makes creating instances of your cells a little bit more simple. Oh, and I also included a template for STKTableViewCells and a script to install that template into Xcode. All of this is located at https://github.com/stablekernel/STKTableViewCell.

Within this repository, you will find a sample project to show off STKTableViewCell. You will notice that there are two subclasses of STKTableViewCell – STKButtonCell and STKWidgetCell – as demonstrations of STKTableViewCell. STKViewController shows you how simple it is to integrate these subclasses into your application.

For your project, you will only need STKTableViewCell.h and STKTableViewCell.m. Drag these files into your project. The easiest way to create a subclass of STKTableViewCell is to use the template I’ve included in the repository.

To install the template, open the Terminal and run

./maketemplate.sh

from the STKTableViewCell root directory.

Now, a STKTableViewCell template will appear in the Cocoa Touch section of iOS in the New File… dialog. You can create your STKTableViewCell subclasses with or without a XIB file. If you choose to use a XIB file, simply drag all of your content views onto the cell in the XIB file and connect them to the instance of the cell in the outline view (not the File’s Owner).

If you choose not use use a XIB file, you may override the method cellDidLoad and layoutContent in your subclass implementation file:

- (void)cellDidLoad
{
    UIButton *b = ...;
    [b addTarget:self action:@selector(buttonTapped:) forControlEvents:UIControlEventTouchUpInside];

    [[self contentView] addSubview:b];
    [self setButton:b];
}

- (void)layoutContent
{
    [[self button] setFrame:...];
}

Each control must send its action message to its cell. However, what we really want is for the controller of the table view to receive this message along with the index path of the control’s cell. In the cell’s implementation file, implement all action methods like so:

- (void)buttonTapped:(id)sender
{
   ROUTE(sender);
}

The ROUTE directive sends a message to the cell which forwards this message to the controller object. Each of your action methods will only ever ROUTE the sender.

In your table view’s controller, you instantiate STKTableViewCell subclasses like so:

- (UITableViewCell *)tableView:(UITableView *)tableView
         cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
   STKButtonCell *c = [STKButtonCell cellForTableView:tableView target:self];
   return c;
}

Note that +cellForTableView:target: will automatically create or reuse an STKButtonCell for you, as well as set up your controller to receive messages when the cell’s button is tapped.

Finally, in order for your controller to receive messages, you will implement a method in your controller’s implementation file that matches the selector of the control’s action message, plus the label atIndexPath:

// In your Controller.m

- (void)buttonTapped:(id)sender atIndexPath:(NSIndexPath *)ip
{
   NSLog(@"%@ was tapped in index path %@", sender, ip);
}

Make sure to include STKTableViewCell.h and .m in your projects!

Joe Conway

Founder at Stable Kernel

Leave a Reply

Your email address will not be published. Required fields are marked *