Have you every been frustrated with the small circular touch area when setting the accessory type of a UITableViewCell to be UITableViewCellAccessoryDisclosureIndicator? Unless you have child fingers, it’s very difficult to select consistently.
Here’s one way to improve the user experience and create the same effect of a larger touch pad like you see on the YouTube application. To do this, you need to implement a custom table view cell. You can either create your own subclass, or you can add subviews to the cell’s contextView. This example shows how to do the latter.
When setting up my UITableViewCells I like to create methods that provide the elements I wish to use to add to the cell’s contentView. Here I am creating a UIButton of type UIButtonTypeDetailDisclosure to be added to each cell in the table. The button possesses an area of 44 x 44 pixels and occupies the right most area of the table view cell that will be 320 pixels wide and 44 pixels tall. Note here that you define a target and action of your own rather than use the callbacks for the accessory type defined in the UITableViewDelegate protocol.
- (UIButton *) getDetailDiscolosureIndicatorForIndexPath: (NSIndexPath *) indexPath { UIButton *button = [UIButton buttonWithType: UIButtonTypeDetailDisclosure]; button.frame = CGRectMake(320.0 - 44.0, 0.0, 44.0, 44.0); [button addTarget:self action:@selector(detailDiscolosureIndicatorSelected:) forControlEvents:UIControlEventTouchUpInside]; return button; }
The following shows how to add the button to the cell in the cellForRowAtIndexPath method in your table view controller class. As you add subviews to the contentView, be sure your detailed disclosure indicator is rendered on top. Also, ensure you are properly handling the reuse of your cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { ... [cell.contentView addSubview: [self getDetailDiscolosureIndicatorForIndexPath: indexPath]]; ... }
Finally to process the selection event you need the following code:
- (void) detailDiscolosureIndicatorSelected: (UIButton *) sender { // // Obtain a reference to the selected cell // UITableViewCell *cell = [self.tableView cellForRowAtIndexPath: [self.tableView indexPathForSelectedRow]]; // // Do something like render a detailed view // ... }
February 16th, 2009 at 23:55
Hello,
Thanks for the code. I’m having some difficulties when the button is actually submitted.
I’m adding the button view to after I’ve subclassed a UITableViewCell with multiple labels. The button appears fine, but I’m unable to return the IndexPath for that selected cell.
I’m using the SeismicXML example to build my UITableView. The code inside the – (void) detailDiscolosureIndicatorSelected: (UIButton *) sender doesn’t allow me to grab any of the data associated with my cell.
Any suggestions? I’m really in need of getting a UIButton added to a UITableViewCell to respond to its own actions.
Thank you,
Mitch
February 17th, 2009 at 08:20
Hi Mitch,
Thanks for the inquiry! One way to go is store the row number in the tag attribute of the button when building the cell. That way you’ll have this data in the sender object. This approach becomes less useful (or more complicated) if your table view possesses more than one section. I’m sure there are more elegant approaches.
Using the code below should work too, but experimenting this morning showed me it does not return the correct data at least for my implementation.
NSIndexPath *indexPath = [tableView indexPathForSelectedRow];
Keep me posted on how it goes!
February 18th, 2009 at 11:02
Thanks for this tip. I used the tag property of the button to store the cell index when I created the button and I was able to use it to trigger an action based on the button’s cell # even though the button is not tied to the cell.
February 28th, 2009 at 19:27
Have you seen the Accessory example code at the SDK.
The IndexPath is got through the touch point at the view.
CGPoint currentTouchPosition = [touch locationInView:self.tableView];
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint: currentTouchPosition];
March 20th, 2009 at 22:38
Jess,
I’m trying to use the tag property as you’ve mentioned above, but can’t get it to work. Would you mind posting the code of how to do this?
After setting the button’s tag value when I create the button, I’ve tried using sender.tag to retrieve the value when the button is pressed but my code won’t compile. I just get a ‘request for member not a structure or union’ error.
Any sample code would be appreciated.
November 17th, 2009 at 22:14
to Gary
First typecast the sender to button
UIButton *button=(UIButton *)sender;
int tag=button.tag;
November 20th, 2009 at 02:25
albertofloyd, thank you very much for your mentioning Accessory example. It exactly solves all problems connected with indexPath and UIButton actions.