TTTableViewController showMenu:forCell: example

iOS SDKA week ago, I was looking for ways in Three20 to create an animated row menu similar to the behavior in the Facebook app where a the table cell switches to a view with a “Like” and a “Comment” button. Upon more investigation, I discovered showMenu:forCell: in TTTableViewController that does exactly what I am looking for. Unfortunately Three20 documentation and samples shed little light on how showMenu:forCell: should be used. I also googled around and found a simple example and found this post on Three20 discussion thread on Google Groups, which was enough to get me started in right direction.

iPhone table view iPhone table view show menu

I messed around with the code further and came up with a more coherent technique in creating the animated show menu effect using showMenu:forCell: Here’s a snippet of the same code demonstrating how you can trigger the menu view by swipe gesture.

- (void) viewDidLoad {
  // Register the swipe gesture.
  UISwipeGestureRecognizer *recognizer = [[UISwipeGestureRecognizer alloc]
                                          initWithTarget:self
                                          action:@selector(swipeDidOccur:)];
  [recognizer setDirection:(UISwipeGestureRecognizerDirectionRight |
                            UISwipeGestureRecognizerDirectionLeft)];
  [self.view addGestureRecognizer:recognizer];
  [recognizer release];

  [super viewDidLoad];
}

- (void) swipeDidOccur:(UISwipeGestureRecognizer *)recognizer {
  // Get the start point so that we can do a hit test on the table view.
  CGPoint startPoint = [recognizer locationInView:self.tableView];
  NSIndexPath *path = [self.tableView indexPathForRowAtPoint:startPoint];
  TTTableViewCell* cell = (TTTableViewCell*) [self.tableView
                                              cellForRowAtIndexPath:path];

  // Add menuView to the cell.
  UIView *menuView = [[UIView alloc] initWithFrame:cell.contentView];

  // Now call showMenu with the menu to display on the associated cell.
  [self showMenu:menuView forCell:cell animated:YES];
  [menuView release];
}
The code relies on detecting swipe touch gesture on TTTableViewController and using a selector to handle the gesture. Given a gesture touch point, you can determine the cell where the swipe occurred. Once you have a reference to a cell, you can call the showMenu:forCell:
What if we need to trigger the menu view by touch a button on the table view cell? This is what I did:
Create a subclass of TTTableViewCell and add a UIButton to the view.

The code relies on detecting swipe touch gesture on TTTableViewController and using a selector to handle the gesture. Given a gesture touch point, you can determine the cell where the swipe occurred. Once you have a reference to a cell, you can call the showMenu:forCell:
What if we need to trigger the menu view by touch a button on the table view cell? This is what I did:

Create a subclass of TTTableViewCell and add a UIButton to the view.

@interface MyViewCell : TTTableViewCell {
}

- (id) initWithName:(NSString *)name target:(id)target action:(SEL)action {
  // ...

  UIButton *moreButton = [UIButton buttonWithType:UIButtonTypeCustom];
  moreButton.frame = CGRectMake(268.0f, 6.0f, 32.0f, 32.0f);
  [moreButton setImage:TTIMAGE(@"bundle://Icon_More.png")
              forState:UIControlStateNormal];
  [moreButton addTarget:target action:action
       forControlEvents:UIControlEventTouchUpInside];
  [self moreButton];

  // ...
}

Next, create a subclass of TTTableViewController and add the custom TTTableViewCell to the data source.

@interface MyTableViewController : TTTableViewController {
}

- (void) createModel {
  self.dataSource = [TTListDataSource dataSourceWithObjects:
    [[[ContactViewCell alloc] initWithName:@"Cell 1"
                                    target:self
                                    action:@selector(plusButtonDidPress:)]
                                    autorelease],
    [[[ContactViewCell alloc] initWithName:@"Cell 2"
                                    target:self
                                    action:@selector(plusButtonDidPress:)]
                                    autorelease],
    nil];
}

In the action handler, that’s where showMenu:forCell: is called. The trick is to determine which cell the button belongs to and consequently replace that cell withe the menu view. This is how I did.

- (void) plusButtonDidPress:(id)sender {
  // Load our custom menu view from a nib.
  UIView *menuView = [[UIView alloc] initWithFrame:cell.contentView];

  UIButton *moreButton = (UIButton *) sender;
  // Convert plusButton bounds to the the coordinate system of table view
  // and then get the cell containing the button.
  CGRect coord = [plusButton convertRect:moreButton.bounds toView:self.tableView];
  NSIndexPath *path = [self.tableView indexPathForRowAtPoint:coord.origin];
  TTTableViewCell* cell = (TTTableViewCell*) [self.tableView
                                              cellForRowAtIndexPath:path];

  // Now call showMenu with the menu to display on the associated cell.
  [self showMenu:menuView forCell:cell animated:YES];
}

Here’s the entire source code on GitHub. Enjoy.

Update

Since some of you asked, I have updated the sample on GitHub to include an example of you can “wire” the buttons on the MenuView to the appropriate action handlers.

First, create a base view controller called BaseCatalogViewController containing the action handlers and have the other 3 controllers inherit the base class. The file’s owner of MenuView.xib should be of type BaseCatalogViewController. Lastly, associate each button in MenuView.xib to an IBAction. You can find the source code at GitHub. Good luck.

  • baba

    Hi Samuel. Really great work! I aprreciate it.

    Do you know how to proceed selecting one of the icons after a row is selected?
    I want for example to do a concrete action after clicking the mail icon.

    thanks in advance.
    best,

    • Anonymous

      Thanks for the feedback. I have updated the sample on GitHub to include an example of you can “wire” the buttons on the MenuView to the appropriate action handlers. First, create a base view controller called BaseCatalogViewController containing the action handlers and have the other 3 controllers inherit the base class. The file’s owner of MenuView.xib should be of type BaseCatalogViewController. Lastly, associate each button in MenuView.xib to an IBAction. You can find the source code at http://bit.ly/iXUiqD. Good luck.