Materials for my iOS frameworks talk at MobileCamp Boston

Presentation Screen IconI will be speaking at MobileCamp Boston on Feb 19, 2011 (later today). I will be giving a presentation on building RESTful iOS apps using Three20 and RESTKit frameworks. I will write a blog entry with a more detailed coverage of my talk later. For now, here are the presentation slides and the TwitterReader Source Code I will use at the talk. Enjoy.

Icon credits: Jack Cai

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.

Three20 is alive

Three20 I raised concerns about the general lack of direction and the confusing state of the Three20 framework with this blog post a week ago. After a long period of silence, it looks like Jeff Verkoeyen, one of the lead contributors of Three20, has spoken. He made this post on Three20 discussion group last night. He is back on the project after a hiatus and will be devoting personal time (10-15 hours/week) towards the Three20 project.

Some highlights of the post include:

  • Clean up the morass of websites that the Three20 project team maintains
  • Clean up the code and migrate to a modular model
  • Call for more content contributors and coders to help grow the project

Awesome, I have really gotten used to the Three20 programming model and API after 3 projects, and it’s good to know that the project is alive and going somewhere. All this is good news. Welcome back Jeff.

Concerns about Three20

Yesterday, my company released VSS Food Guide, a vegetarian food guide in Singapore, to Apple iTunes. We are already planning for more app releases in the next few months. However, a technical question for our app development efforts remains: should we continue using Three20, the same framework used to develop Facebook for iPhone. I like the framework, especially its URL-based navigation for navigating a user from one view to another and for persisting an app view state. Because the state of each view is represented by an URL, the entire view state of the app is automatically persisted by saving the URLs of all views when the app exits. Other benefits of Three20 includes a large collection of visual controls, caching, and network utilities. I used Three20 extensively in the VSS Food Guide. The framework is very useful once you get used to the programming model.

Despite all these advantages, I still contemplate using Three20 to develop future apps. The recent discussions in the Three20 forum board echo my concerns. The timing is quite uncanny. The thread expresses concerns over the lack of direction, a general lack of documentation, and fragmentation due to numerous forks of the source. The framework is cool but it can be quite confusing since it isn’t well maintained or documented, which is quite typical of any open-source project at this stage. The good news is that it looks like efforts are being made to steer the project back on course.