4.3. Creating Custom Table View Cell Accessories

Problem

The accessories provided to you by the iOS SDK are not sufficient, and you would like to create your own accessories.

Solution

Assign an instance of the UIView class to the accessoryView property of any instance of the UITableViewCell class:

- (UITableViewCell *)tableView:(UITableView *)tableView
         cellForRowAtIndexPath:(NSIndexPath *)indexPath{

    UITableViewCell* cell = nil;

    cell = [tableView dequeueReusableCellWithIdentifier:MyCellIdentifier
                                           forIndexPath:indexPath];

    cell.textLabel.text = [NSString stringWithFormat:@"Section %ld, Cell %ld",
                             (long)indexPath.section,
                             (long)indexPath.row];

    UIButton *button = [UIButton buttonWithType:UIButtonTypeSystem];
    button.frame = CGRectMake(0.0f, 0.0f, 150.0f, 25.0f);

    [button setTitle:@"Expand"
            forState:UIControlStateNormal];

    [button addTarget:self
               action:@selector(performExpand:)
     forControlEvents:UIControlEventTouchUpInside];

    cell.accessoryView = button;

    return cell;

}

As you can see, this code uses the performExpand: method as the selector for each button. Here is the definition of this method:

- (void) performExpand:(UIButton *)paramSender{

    /* Handle the tap event of the button */

}

This example code snippet assigns a custom button to the accessory view of every row in the targeted table. The result is shown in Figure 4-4.

Table view cells with custom accessory views

Figure 4-4. Table view cells with custom accessory views

Discussion

An object of type UITableViewCell retains a property named accessoryView. This is the view you can assign a value to if you are not completely happy with the built-in iOS SDK table view cell accessories. After this property is set, Cocoa Touch will ignore the value of the accessoryType property and will use the view assigned to the accessoryView property as the accessory assigned to the cell.

The code listed in this recipe’s Solution creates buttons for all the cells populated into the table view. When a button is pressed in any cell, the performExpand: method gets called, and if you are like me, you have probably already started thinking about how you can determine which cell the sender button belongs to. So now we have to somehow link our buttons with the cells to which they belong.

One way to handle this situation is to take advantage of the tag property of the button instance. The tag property is a simple integer that people usually use to associate a view with another object. For instance, if you want to associate the button with the third cell in your table view, set the value of the button’s tag property to 3. But there is a problem here: table views have sections, and every section can have n number of cells. We, therefore, have to be able to determine the section as well as the cell that owns our button, and since the tag can represent only one integer, this makes things more difficult. Instead of a tag, therefore, we can ask for the superview of the accessory view, going recursively up the chain of views until we find the cell of type UITableViewCell, like so:

- (UIView *) superviewOfType:(Class)paramSuperviewClass
                     forView:(UIView *)paramView{

    if (paramView.superview != nil){
        if ([paramView.superview isKindOfClass:paramSuperviewClass]){
            return paramView.superview;
        } else {
            return [self superviewOfType:paramSuperviewClass
                                 forView:paramView.superview];
        }
    }

    return nil;

}

- (void) performExpand:(UIButton *)paramSender{

    /* Handle the tap event of the button */
    __unused UITableViewCell *parentCell =
        (UITableViewCell *)[self superviewOfType:[UITableViewCell class]
                                         forView:paramSender];

    /* Now do something with the cell if you want to */

}

This is a simple recursive method that accepts a view (in this case our button) and a class name (in this case, UITableViewCell), then searches in the view’s super view hierarchy to find the super view that is of the given class. So it starts with the super view of the given view, and if that super view is not of the required type, looks at the super view’s super view, and so on until it finds the super view of the requested class. You can see that we are using the Class structure as the first parameter to the superviewOfType:forView: method. This data type can hold any Objective-C class name, and it’s great if you are looking for or asking for specific class names from the programmer.

Get iOS 7 Programming Cookbook now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.