Sections and Indexing Tables

If you look at Phoneapp or Contactsapp, you’ll notice that the alphabetical listing of your contacts has sticky headers at the top of the screen that indicate which part of the list you’re viewing. These are the default UITableView section dividers. You’ll also notice a bar on the right side that acts as a quick index for these sections, which is another built-in element of sections in UITableView. We’re going to learn about both and add them to our app.

Adding sections to a table is pretty easy: implement a few more methods and add some additional logic in the existing dataSource implementations. Ideally, your data should be structured as a hash where the keys are your section titles and the values are rows contained in that section. Take our alphabet as an example.

 @data = {
 "A"​ => [​"Adam"​, ​"Apple"​],
 "B"​ => [​"Barry"​, ​"Berry"​],
 "C"​ => [​"Carlos"​, ​"Charlie"​],
  ...
 }

You get the idea. Your data doesn’t need to be a hash, but you should have a way of referencing sections by their numerical index and then accessing the row data inside each section using a secondary index.

It’s often useful to just create some helper methods in your dataSource so you don’t repeat a lot of the same lookup code, which can be a pain if you change your structure later. In the AlphabetController, add the following:

 def​ sections
  @data.keys.sort
 end
 
 def​ rows_for_section(section_index)
  @data[self.sections[section_index]]
 end
 
 def​ row_for_index_path(index_path)
  rows_for_section(index_path.section)[index_path.row]
 end

These just abstract NSIndexPath-based access to our data. Again, it’s not absolutely necessary, but if we ever change our data structure, then we would need to change only these three methods.

Now we need to implement a new dataSource method, numberOfSectionsInTableView:, which returns exactly what it says. Using the previous convenience methods, we implement it like so:

 def​ numberOfSectionsInTableView(tableView)
  self.sections.count
 end

Now we need to go back and update our original dataSource and delegate methods to use the new data structure. These are all pretty easy changes thanks to our helper methods.

 def​ tableView(tableView, ​numberOfRowsInSection: ​section)
» rows_for_section(section).count
 end
»cell.textLabel.text = row_for_index_path(indexPath)
 
 cell
 def​ tableView(tableView, didSelectRowAtIndexPath​:indexPath​)
  tableView.deselectRowAtIndexPath(indexPath, ​animated: ​​true​)
 
» letter = sections[indexPath.section]
 
  controller = UIViewController.alloc.initWithNibName(​nil​, bundle​:nil​)
  controller.view.backgroundColor = UIColor.whiteColor
 
  controller.title = letter
 
  label = UILabel.alloc.initWithFrame(CGRectZero)
» label.text = row_for_index_path(indexPath)
  label.sizeToFit

Finally, to get the gray headers to appear, we need to return their titles in another new dataSource method, tableView:titleForHeaderInSection::

 def​ tableView(tableView, titleForHeaderInSection​:section​)
  sections[section]
 end

Wait a second, we’ve implemented the effects of changing the structure of @data, but we haven’t actually changed it! If you feel compelled, you can use a list of actual names like in the earlier example, but we’re going to use some Ruby tricks and just make random strings for each letter.

 @data = {}
 (​"A"​..​"Z"​).to_a.each ​do​ |letter|
  @data[letter] = []
  5.times ​do
 # Via http://stackoverflow.com/a/88341/910451
  random_string = (0...4).map{65.+(rand(25)).chr}.join
  @data[letter] << letter + random_string
 end
 end

Boy, that is a lot of stuff to digest. The important part is that we set our keys of @data to be a letter and its values to be arrays of strings:

images/tables/table_sections.png

You could just as well have populated the array with letter five times, but the random string is a neat trick.

If you rake now, you can play with our gray headers.

But we’re not done! In the iPod and Contacts apps, you’ll notice there’s a nifty scroller on the right side. This is called the index, and its values correspond to the section titles. Adding one to a table is really easy; we need only two new methods.

 def​ sectionIndexTitlesForTableView(tableView)
  sections
 end
 def​ tableView(tableView, ​sectionForSectionIndexTitle: ​title, ​atIndex: ​index)
  sections.index title
 end

sectionIndexTitlesForTableView: should return the array of strings to be shown on the index (["A", "B", "C"...."Z"]).

sectionForSectionIndexTitle: returns what section the table should jump to when a given title is tapped on the index. So, if we tap C on the index, the table view will call dataSource.tableView(self, sectionForSectionIndexTitle: "C", atIndex: 2). We could just return index, since we have a 1:1 map of sections to indices, but this is an implementation that works if that isn’t true.

If you rake now, you should see our section index on the right side:

images/tables/table_index.png

Our table is now interactive and user-friendly, but it’s still static data. Let’s make it more dynamic by allowing our alphabet-learner to delete the rows he or she doesn’t find useful.

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

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