Chapter 12: Common UI Paradigms Using Table Views

In Chapter 6, I introduced table views and showed you how to get a good scrolling performance when you use table views. Apart from showing a list of data, table views are also used as a substitute for creating complex structured scrollable views, and in fact most of the time, table views are used as cheap substitutes for creating vertically scrollable views even if the content they display is not a list of data. For example, in the built-in contacts app, the contacts list is a UITableView, and so is the view for adding a new contact. Additionally, third-party application developers have introduced new interaction patterns, and they have been commonly used on other apps as well.

This chapter focuses on the advanced aspects of table views and shows you how to create complex (yet common) UIs such as Pull-To-Refresh and infinite scrolling lists. It also briefly explains how to use table view row animations to create accordion and options drawer (a UI that shows available toolbar elements just below the table view cell that is acted upon) and several other interesting UI paradigms.

iOS has been around for more than five years, so this chapter is based on the assumption that you’re well versed in concepts like UITableViewDelegate and UITableViewDataSource. In addition, though you don’t have to read this book sequentially, this chapter is also based on the assumption that you have read Chapter 6, which as I just mentioned, introduces you to table views.

If you’re not familiar with UITableViewDelegates and UITableViewDataSource, I suggest you read Chapter 8 in Beginning iPhone Development: Exploring the iPhone SDK by Dave Mark and Jeff Lamarche (Apress 2009, ISBN 978-1430216261) before finishing this chapter.

Pull-To-Refresh

Pull-to-Refresh is an interesting UI paradigm that was invented (and patented) by Loren Brichter. Prior to iOS 6, several third-party developers such as Dr. Touch (Cocoanetics) and enormego released code that allowed you to implement Pull-to-Refresh in your own applications. With iOS 6, Apple added it as a built-in feature of UITableViewController, and you can use the all-new UIRefreshControl to add Pull-to-Refresh support to your table view controllers.

In this section, I show you how to write a PullToRefreshTableView class based on enormego’s excellent open source implementation (see the “Further Reading” section at the end of this chapter). This class isolates most of the Pull-To-Refresh code into a superclass, and later on, when you need to add a Pull-To-Refresh feature to your table view, all you need to do is inherit your view controller from PullToRefreshTableViewController instead of UIViewController and override methods to perform the actual refresh. Later, I’ll show you how to implement Pull-to-Refresh using the built-in UIRefreshControl in iOS 6. After explaining both the techniques, I’ll show you how to selectively use UIRefreshControl on iOS 6 and fall back gracefully to the previous implementation on devices running the older operating system.

Now, it’s time delve into the code. First, download the files for this chapter from the book’s website.

Create a project using the Master/Detail template and add these files:

EGORefreshTableHeaderView.h

EGORefreshTableHeaderView.m

PullToRefreshViewController.h

PullToRefreshViewController.m

RefreshArrow.png

[email protected]

The PullToRefreshTableViewController is a subclass of UITableViewController that abstracts the mechanics behind Pull-To-Refresh. It handles the UIScrollView delegates and adds the EGORefreshHeaderView to the top of your UITableView when the table view is pulled beyond a certain threshold. It also remembers the last refreshed state. By default, the last refreshed state is stored in a key that uses your subclass name and a suffix string. In case this isn’t enough and you have multiple instances of the same class displaying different data, you can customize the key in which the last refreshed date is remembered. The key is stored in a property called keyNameForDataStore.

To implement Pull-To-Refresh in your code, inherit the MasterViewController (the controller that is originally inheriting from UITableViewController) from PullToRefreshViewController and override the doRefresh method to perform the actual refresh. Once the refresh is done, set the loading state to NO. It’s as simple as that. The PullToRefreshViewController also needs you to link your target with QuartzCore.Framework.

When you inherit your view controller from PullToRefreshViewController, you will see a view (from the superclass UITableViewController) in the IBOutlet list in IB. Connect this view to the table in your nib file.

Now, in the controller, override the doRefresh method and perform your network call (or any time-consuming refresh operation). When the refresh operation is complete, set the loading state to NO.

Following is the sample code snippet for your view controller.

Sample doRefresh Implementation

-(void) doRefresh {

// Do your time-consuming operation here.

// The dispatch_after is just for your illustration

  double delayInSeconds = 2.0;

  dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW,

   delayInSeconds * NSEC_PER_SEC);

  dispatch_after(popTime, dispatch_get_main_queue(), ^(void){

    self.loading = NO;

// the loading property is exposed by

PullToRefreshViewController. When you set this to NO,

it restores the tableview back to its normal position.

  });

}

iOS Pull-to-Refresh

With iOS 6, Apple introduced a new control called UIRefreshControl that can be attached to any UITableViewController. Implementing the iOS 6 Pull-To-Refresh couldn’t be easier. Apple has abstracted away nearly everything so that you only need to instantiate a UIRefreshControl and set it to the refreshControl property of your UITableViewController. Handle the events sent by the refresh control to do the actual refresh. The following lines of code illustrate the use of UIRefreshControl for implementing Pull-to-Refresh.

Pull-To-Refresh Using UIRefreshControl

self.refreshControl = [[UIRefreshControl alloc] init];

    [self.refreshControl addTarget:self

     action:@selector(refreshedByPullingTable:)

     forControlEvents:UIControlEventValueChanged];

// handler that gets called when the refresh control is pulled

-(void) refreshedByPullingTable:(id) sender {

  

  [self.refreshControl beginRefreshing];

  double delayInSeconds = 2.0;

  dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW,

   delayInSeconds * NSEC_PER_SEC);

  dispatch_after(popTime, dispatch_get_main_queue(), ^(void){

    [self.refreshControl endRefreshing];

  });

}

Note that dispatch_after is used here for simulating a delay. You need to replace it with your time-consuming network operation. The most important lines are shown in bold. That was easy, wasn’t it? The (slightly) hard part is to support iOS 5 and fall back to enormego’s mechanism while showing the cool iOS 6-style refresh control on supported devices.

You do this by selectively instantiating a UIRefreshControl or enormego’s EGORefreshHeaderView in the viewDidLoad method of the PullToRefreshViewController, as shown here:

if(NSClassFromString(@”UIRefreshControl”)) {

    

    self.refreshControl = [[UIRefreshControl alloc] init];

    [self.refreshControl addTarget:self

     action:@selector(refreshedByPullingTable:)

     forControlEvents:UIControlEventValueChanged];

  } else {

    

    self.refreshHeaderView = [[EGORefreshTableHeaderView alloc]

    initWithFrame:CGRectMake(0.0f,

    0.0f - self.tableView.bounds.size.height,

    self.tableView.bounds.size.width,

    self.tableView.bounds.size.height)];

    

    self.refreshHeaderView.keyNameForDataStore = [NSString

    stringWithFormat:@”%@_LastRefresh”, [self class]];

    self.tableView.showsVerticalScrollIndicator = YES;

    [self.tableView addSubview:self.refreshHeaderView];

  }

Well, that wasn’t hard. Right? The complete code is available in the code download for this chapter on the book’s website. When you use this code, you don’t have to take care of iOS 5/iOS 6 switching. If you’ve been using the previous version of the code (from iOS 5 Programming Pushing the Limits), replacing the files with the newer version will automatically handle the switching. The previous section explains how this is done internally in PullToRefreshViewController.

enormego did an excellent job of writing the mechanics behind Pull-to-Refresh. This takes it to the next level by abstracting the logic out and providing a super-easy way to implement it in any of your view controllers with under five lines of code.

Now, it’s time to find out about another cool UI paradigm: infinite scrolling.

Infinite Scrolling

Infinite scrolling is normally used in Twitter clients or any app that displays chronologically ordered data. You generally use infinite scrolling when you don’t know the number of items in the dataset or when the number of items is immensely large (unlike a contacts list).

You will now implement infinite scrolling support to the PullToRefreshViewController written in the previous section. The superclass, PullToRefreshViewController, adds a section to the end of your table view that shows a single loading cell. You implement this by adding a couple of properties called numberOfSections and endReached to the class PullToRefreshTableView:

@property (nonatomic) NSInteger numberOfSections;

@property (nonatomic) BOOL endReached;

You then add a method, loadMore, that is called when the user reaches the end of the current page in the table view. The superclass implementation for the loadMore method will be empty, and you will leave that for the subclasses to implement. Essentially, the super class adds a new section to your tableview and adds a loading cell and calls the loadMore method. The complete code can be found in the code download for this chapter, in the Chapter 11/InfiniteScrollingExample folder.

Implementing the infinite scrolling code in your app is easy. Override the method loadMore (defined in the superclass) and provide implementation for loading more content. Do not implement the numberOfSectionsInTableView: in your subclass. The superclass (PullToRefreshViewController) does this automatically for you. Instead, set the number of sections using the superclass property numberOfSections. The superclass adds an additional section to the end of your table to show the loading cell. When your server returns no content, you can set the endReached property to YES. This prevents the loading cell from being shown again. The following sample code snippet explains this.

Sample loadMore Implementation

-(void) loadMore {

  

  double delayInSeconds = 2.0;

  dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW,

   delayInSeconds * NSEC_PER_SEC);

  dispatch_after(popTime, dispatch_get_main_queue(), ^(void){

    self.pageCount ++;

    if(self.pageCount == 5) self.endReached = YES;

    [self.tableView reloadData];

  });

}

The superclass adds a loading section as the last section of the table view, and your table view data source methods will be called for sections you’re not aware of. You need to forward these calls to the superclass of the tableView:numberOfRowsInSection: method and tableView:cellForRowAtIndexPath: for sections greater than your section count. In other words, let the superclass handle sections greater than the numberOfSections for you. This implementation shows the loadingCell.

The following code snippet explains this.

Sample TableView Data Source

- (NSInteger)tableView:(UITableView *)tableView

    numberOfRowsInSection:(NSInteger)section {

    // Return the number of rows in the section.

    if(section == self.numberOfSections)  {

        return [super tableView:tableView numberOfRowsInSection:section];

    }

    return 20 * self.pageCount; // we are assuming 20 rows per page

}

- (UITableViewCell *)tableView:(UITableView *)tableView

cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    

    if(indexPath.section == self.numberOfSections)  {

        return [super tableView:tableView

cellForRowAtIndexPath:indexPath];

    }

    static NSString *CellIdentifier = @”Cell”;

    

    UITableViewCell *cell =

[tableView dequeueReusableCellWithIdentifier:CellIdentifier];

cell.textLabel.text = [NSString stringWithFormat:

@”Row %d”, indexPath.row];

    cell.detailTextLabel.text = [NSString stringWithFormat:

@”Row %d”, indexPath.row];

    

    cell.imageView.image = [UIImage imageNamed:@”ios6”];

    cell.selectionStyle = UITableViewCellSelectionStyleNone;

    return cell;

}

That completes it. With very few changes, you’ve added infinite scrolling support to the Pull-to-Refresh example code. Implementing infinite scrolling in your apps couldn’t be easier.

Inline Editing and Keyboard

Form-filling is a common UI pattern found on both web and mobile environments. On iOS, forms are usually implemented using UITableView, with each cell representing one data entry field. For reasons stated in Chapter 6, a table view is preferred rather than a scroll view in these scenarios.

The most important point to remember here is to show data entry fields above the keyboard when the keyboard is shown. To do so, you need to adjust your table views dynamically.

If your table view contains data entry fields such as a UITextField or a UITextView and the table view is long enough to cover the screen, you’ll have a problem accessing data entry fields that are hidden by the keyboard. The easiest—and recommended—way to overcome this problem is to use a UITableViewController for your form. Otherwise, if you use a UIViewController and a UITableView as its subview, you must explicitly code for scrolling your UI so that elements that might get hidden by the keyboard stay visible.

You can scroll your UI’s frame by observing the UIKeyBoardDidShowNotification and UIKeyBoardDidHideNotification. The notification posts an object (NSDictionary) containing information pertaining to the size of the keyboard, the animation duration, and the animation curve.

The following code snippet illustrates this in practice.

In viewDidLoad or one of the init methods, add these observer keyboard notifications and remove them in the didReceiveMemoryWarning method:

  [[NSNotificationCenter defaultCenter]

   addObserver:self selector:@selector(keyboardWillShow:)

   name:UIKeyboardWillShowNotification object:nil];

  

  [[NSNotificationCenter defaultCenter]

   addObserver:self selector:@selector(keyboardWillHide:)

   name:UIKeyboardWillHideNotification object:nil];

Then adjust the table view’s height accordingly in the selectors. You can get the size of the keyboard, the animation curve, and the animation duration from the notification object, as illustrated in the following code:

-(void) keyboardWillShow:(NSNotification*) notification {

  

  CGRect keyboardFrame;

  [[notification.userInfo valueForKey:UIKeyboardFrameEndUserInfoKey]

    getValue: &keyboardFrame];

  keyboardFrame = [self.view convertRect:keyboardFrame toView:nil];  

  double keyboardHeight = keyboardFrame.size.height;

  

  double animationDuration;

  UIViewAnimationCurve animationCurve;

  

  [[notification.userInfo valueForKey:UIKeyboardAnimationDurationUserInfoKey]

    getValue: &animationDuration];

  [[notification.userInfo valueForKey:UIKeyboardAnimationCurveUserInfoKey]

    getValue: &animationCurve];

  

  [UIView animateWithDuration:animationDuration

                        delay:0.0f

                      options:animationCurve

                   animations:^{

                     

                     CGRect frame = self.tableView.frame;

                     frame.size.height -= keyboardHeight;

                     self.tableView.frame = frame;

                   } completion:^(BOOL finished) {

                     

                   }];

}

-(void) keyboardWillHide:(NSNotification*) notification {

  

  CGRect keyboardFrame;

  [[notification.userInfo valueForKey:UIKeyboardFrameEndUserInfoKey]

    getValue: &keyboardFrame];

  keyboardFrame = [self.view convertRect:keyboardFrame toView:nil];  

  double keyboardHeight = keyboardFrame.size.height;

  

  double animationDuration;

  UIViewAnimationCurve animationCurve;

  

  [[notification.userInfo valueForKey:UIKeyboardAnimationDurationUserInfoKey]

    getValue: &animationDuration];

  [[notification.userInfo valueForKey:UIKeyboardAnimationCurveUserInfoKey]

    getValue: &animationCurve];

  

  [UIView animateWithDuration:animationDuration

                        delay:0.0f

                      options:animationCurve

                   animations:^{

                     

                     CGRect frame = self.tableView.frame;

                     frame.size.height += keyboardHeight;

                     self.tableView.frame = frame;

                   } completion:^(BOOL finished) {

                     

                   }];

}

Also note that, as the table view is scrolled, your cells are recycled and reused. Any data entered is lost when the cell is recycled, so you need to copy the entered data from the UI to your model classes (or NSString) immediately after the entry is made, which is quite easy to do. One way is to set the delegate of your UITextField to the table view controller and handle textFieldDidEndEditing. But a good design practice is to let the table cell handle the delegate and notify its superclass. (You find out more about such best practices throughout this chapter.) The superclass saves the data to the corresponding model object and prepopulates the table view cell with values from the model when it’s created or dequeued in cellForRowAtIndexPath.

The following code segment shows you how to save data entered in a table view cell.

Saving and Restoring Data from UITextField Inside a Custom UITableViewCell

cell.inputText.text = [self.data objectAtIndex:indexPath.row];

    cell.onTextEntered = ^(NSString* enteredString) {

        

        [self.data insertObject:enteredString

atIndex:indexPath.row];

    };

This code assumes that the cell handles IBAction and textFieldDidEndEditing and passes the enteredString value to the table view controller using a block. The data entered is stored in a member variable (data) and is restored in the first line. Two lines and that’s it. You can use delegates as well, but blocks are cleaner and result in much less code. (You discover more about blocks in Chapter 23.)

Animating a UITableView

You’ve now seen some practical implementations of UITableView. Now, you go to the next level by finding out how to make the best use of the animations a table view provides. The UIKit framework provides some default animation styles for animating rows in a UITableView. These animations play a very important role in giving subtle hints to the user about what’s happening behind the scenes.

A good example is the phone app on your iPhone. When you toggle between all calls and missed calls, the complete list animates to show or hide the relevant data. Similarly, on the settings app, when you turn on Airplane mode, the Carrier row hides because it’s no longer relevant. But if these actions happen without animations, users will be confused about what’s happening. One thing that sets iOS apart from its competitor is that creating a compelling user experience that blends well with the OS is easy. In this case, implementing these animations is very easy with UITableView. Using methods in UITableView, you can animate a row insertion, row deletion, or row updates with fewer than ten lines of code.

The most important thing to remember here is that prior to your table updates, you need to update your models. Failure to do so will result in an NSInteralInconsistencyException (crash). In other words, if you’re displaying a list of items, you need to insert a new row in the table view after the model is updated.

Remember, if you have to perform a batch of animated updates on UITableView, you can sandwich them between calls to beginUpdates and endUpdates. iOS automatically computes the changes and performs the correct animation sequence for you. The following are the commonly used methods for performing animated updates to a UITableView:

insertRowsAtIndexPaths:withRowAnimation:

deleteRowsAtIndexPaths:withRowAnimation:

reloadRowsAtIndexPaths:withRowAnimation:

In the following list, the first parameter is the array of index paths you need to add, and the second is the animation style that should be used. The animation style can be one of the following values

UITableViewRowAnimationFade, UITableViewRowAnimationNone

UITableViewRowAnimationRight, UITableViewRowAnimationLeft

UITableViewRowAnimationTop, UITableViewRowAnimationBottom

UITableViewRowAnimationMiddle

UITableViewRowAnimationAutomatic

From iOS 5, you can use a new style, UITableViewRowAnimationAutomatic, and the system automatically chooses the correct animation for you. iOS 5 also introduces two new methods to move a complete section from one location to another. This is helpful in case you want to visually show movement of a complete section.

The following are methods for moving rows and/or sections in a UITableView:

moveSection: toSection:

moveRowAtIndexPath: toIndexPath:

Partially Reloading Tables

You can use the reloadRowsAtIndexPaths:withRowAnimation: method to partially reload a table view. For example, if you get a push notification that data currently displayed on the table view should be updated, you can reload just that single row in a UITableView. I recommend using the UITableViewRowAnimationFade or UITableViewRowAnimationNone style on iOS 4 and earlier and UITableViewRowAnimationAutomatic on iOS 5 for the animation style.

Practical Implementations of Table View Animations

With the built-in UITableView animations, you can easily implement custom controls such as accordion or show and hide drawers that expose additional controls. The next sections provide some ideas for implementing them. Custom controls like these can be implemented in multiple ways. So, instead of focusing on code, you find out about the process behind building them.

Implementing an Accordion List

Accordion is a control often found on content-rich websites to categorize navigational links. It contains a list of sections and subitems under each section. Sections can be opened to reveal the items within and can be closed or collapsed to hide them. On iOS, accordions are often used to model a single-level hierarchical navigation menu. An example is the USA Today app’s Pictures tab. I’ll dissect the view and analyze how a control like that can be created.

From the UI, it appears that the section headers are tappable and that every section has either one row or zero rows based on whether it is in an expanded state or not. This means you need a custom section view that tells the parent controller (your table view controller) that it was tapped.

For this example, design a custom UIView that has one big tappable button. You will use this view as the custom section view for your table. Override the tableView:viewForHeaderInSection: method, create your UIView, and return it. These views will notify (via a delegate or handler) the table view of the button-tapped event back to the table view. On this handler, the table view controller will do two things. First, it updates the models and then the table view. For updating models, you can save the tapped section’s index as the currently expanded index. After this is done, you can refresh the table view in one of two ways: either by firing reloadData to the table view or by calculating the changes and calling the necessary insertRowsAtIndexPaths:withRowAnimation: and deleteRowsAtIndexPaths:withRowAnimation: methods. The reload data method refreshes the entire table, and users will not know what happened behind the scenes. For getting the accordion-like UI effect, you call deleteRowsAtIndexPaths:withRowAnimation: for the old section (currently expanded row) and addRowsAtIndexPaths:withRowAnimation: for the tapped section. Because you’re doing two operations on the table view and you don’t want the table to update itself for every operation, you sandwich them between the methods beginUpdates and endUpdates.

The most complicated part here is matching the changes to the model and the UI synchronously. If your model doesn’t exactly reflect the UI, your code will crash with an NSInternalInconsitencyException.

In a nutshell, the following code explains how to “expand” your accordion table view. The complete code, Ch11/MKAccordion, is available on the book’s website.

Opening an accordion

-(void) openAccordionAtIndex:(int) index {

  

  NSMutableArray *indexPaths = [NSMutableArray array];

  

  int sectionCount = [[self.objects objectForKey:[[self.objects

   allKeys] objectAtIndex:self.currentlyExpandedIndex]] count];

  

  for(int i = 0; i < sectionCount; i ++) {

    

    NSIndexPath *indexPath = [NSIndexPath indexPathForRow:i

     inSection:self.currentlyExpandedIndex];

    [indexPaths addObject:indexPath];

  }

  

  self.currentlyExpandedIndex = -1;

  

  [self.tableView deleteRowsAtIndexPaths:indexPaths

                        withRowAnimation:UITableViewRowAnimationTop];

  

  self.currentlyExpandedIndex = index;

  

  sectionCount = [[self.objects objectForKey:[[self.objects

   allKeys] objectAtIndex:self.currentlyExpandedIndex]] count];

  

  [indexPaths removeAllObjects];

  for(int i = 0; i < sectionCount; i ++) {

    

    NSIndexPath *indexPath = [NSIndexPath indexPathForRow:i

    inSection:self.currentlyExpandedIndex];

    [indexPaths addObject:indexPath];

  }

  

  double delayInSeconds = 0.35;

  dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW,

   delayInSeconds * NSEC_PER_SEC);

  dispatch_after(popTime, dispatch_get_main_queue(), ^(void){

    [self.tableView insertRowsAtIndexPaths:indexPaths

                          withRowAnimation:UITableViewRowAnimationFade];

  });

  

  [self.tableView endUpdates];

}

As stated previously, table views are powerful enough to create highly customized user interfaces. The good news is that the coding part is simple. In the next section, I’ll show you how to implement a drawer control.

Implementing a Drawer

Implementing a drawer-like UI is quite similar to the accordion you implemented in the previous section. A drawer is a unique row in the table view that, instead of showing data, shows tools to manipulate the data. The Twitter client Tweetbot (and many other apps) uses this to show context-sensitive menu options for a table view row.

Implementing a drawer is slightly easier (programmatically) than implementing an accordion. Create a custom UITableViewCell for your drawer. Next, maintain an NSIndexPath pointer that will store the currently tapped row. The following code explains this process.

Drawer Implementation

@property (strong, nonatomic) MKControlsCell *controlsCell;

@property (strong, nonatomic) NSIndexPath *selectedIndexPath;

Now, in tableView:numberOfRowsInSection:, return an additional row if your drawer needs to be displayed.

- (NSInteger)tableView:(UITableView *)tableView

   numberOfRowsInSection:(NSInteger)section

{

  if(self.selectedIndexPath)

    return self.objects.count + 1;

  else

    return self.objects.count;

}

tableView:cellForRowAtIndexPath: returns the control cell for the selected row and returns a data cell for all other rows, as explained in the following code.

tableView:cellForRowAtIndexPath: Method

if(self.selectedIndexPath.row == indexPath.row && self.selectedIndexPath.

     section == indexPath.section && self.selectedIndexPath) {

    

    return self.controlsCell;

  } else {

    

    UITableViewCell *cell = [tableView

    dequeueReusableCellWithIdentifier:@”Cell”];

    if(self.selectedIndexPath && self.selectedIndexPath.row < indexPath.row)

      cell.textLabel.text = self.objects[indexPath.row-1];

    else

      cell.textLabel.text = self.objects[indexPath.row];

    return cell;

  }

Update the selectedIndexPath when a row is selected (tableView:didSelectRowAtIndexPath:). Insert a new row below the selected row and remove the previously added drawer (if any) using insertRowsAtIndexPaths:withRowAnimation: methods.

Now comes the tricky part. Use dispatch_after to time the animation so that it’s “just nice.”

double delayInSeconds = 0.0;

  BOOL shouldAdjustInsertedRow = YES;

  if(self.selectedIndexPath) {

    NSArray *indexesToDelete = @[self.selectedIndexPath];

    if(self.selectedIndexPath.row <= indexPath.row)

      shouldAdjustInsertedRow = NO;

    self.selectedIndexPath = nil;

    delayInSeconds = 0.2;

    [tableView deleteRowsAtIndexPaths:indexesToDelete

      withRowAnimation:UITableViewRowAnimationAutomatic];

  }

  dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW,

   delayInSeconds * NSEC_PER_SEC);

  dispatch_after(popTime, dispatch_get_main_queue(), ^(void){

    if(shouldAdjustInsertedRow)

      self.selectedIndexPath = [NSIndexPath indexPathForRow:indexPath.row + 1 inSection:indexPath.section];

    else

      self.selectedIndexPath = indexPath;

    [tableView insertRowsAtIndexPaths:@[self.

     selectedIndexPath] withRowAnimation:UITableViewRowAnimationAutomatic];

  });

That’s it. The complete code is also available on the book’s website (Chapter 11/MKDrawerDemo).

Using Gesture Recognizers in Table View Cells

Swipe gestures like the swipe-to-delete or swipe-to-reveal options on Twitter for iPhone are another interesting type of interaction pattern. With gesture recognizers introduced in iOS 3.2, you can attach a swipe gesture recognizer (UISwipeGestureRecognizer) to your table cells’ contentView. Attaching a long press gesture recognizer (UILongPressGestureRecognizer) can help in showing a context-sensitive menu (using a UIActionSheet) for a given table view cell element.

Summary

This chapter discussed some of the advanced table view concepts and how to create custom controls like accordions and drawers using table views. Table views can be used for purposes other than just displaying a vanilla list of data. The examples in this chapter unleashed the power of table views and explained how to create interesting controls and interaction paradigms. Customizing a table view is fairly easy, and you can invent your own interaction patterns and a wealth of UI elements similar to the two illustrated in this chapter.

Further Reading

Apple Documentation

The following documents are available in the iOS Developer Library at developer.apple.com or through the Xcode Documentation and API Reference.

What’s New in iOS 6

TableView Programming Guide: “iOS Developer Documentation”

TableViewSuite

UIViewController Programming Guide: “iOS Developer Documentation (Storyboards)”

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.143.4.181