Starting with Simple Rows

For now, our task is simple: display a table with all the letters of the alphabet:

images/tables/table_basic.png

Last I checked there are twenty-six of them, so that’s enough to show off the fancy row reuse that happens with table views. The table is going to be part of our app’s lone controller, so there isn’t a lot of setup to worry about this time.

First we create a new project called TableFun, and the controllers folder in ./app. Our controller will be AlphabetController, so let’s store it in ./app/controllers/alphabet_controllerrb. Our initial implementation should look like this:

 class​ AlphabetController < UIViewController
 def​ viewDidLoad
 super
  self.title = ​"Alphabet"
  @table = UITableView.alloc.initWithFrame(self.view.bounds)
  @table.autoresizingMask = UIViewAutoresizingFlexibleHeight
  self.view.addSubview(@table)
 end
 end

Looks like a typical controller so far, doesn’t it? We store our table view in the @table instance variable and add it like a normal subview, sized to fit the controller. The one new thing here is autoresizingMask, which is a bitmask of UIViewAutoResizing constants. These are used to define how a view changes when its parent view is being resized; in this case, we allow the height of the table to be flexible.

Next, we need to set up a window and add the AlphabetController as its root controller. Let’s mosey on over to AppDelegate and add our simple setup.

 class​ AppDelegate
 def​ application(application, didFinishLaunchingWithOptions​:launchOptions​)
  @window = UIWindow.alloc.initWithFrame(UIScreen.mainScreen.bounds)
  @alphabet_controller = AlphabetController.alloc.initWithNibName(​nil​, bundle​:nil​)
  @window.rootViewController =
  UINavigationController.alloc.initWithRootViewController(@alphabet_controller)
  @window.makeKeyAndVisible
 true
 end
 end

This actually adds it as the root of a UINavigationController, which gives it a nice UI. If you rake now, you’ll see a lovely but empty table view. Let’s fill that with some data.

First we need to set our AlphabetController as the table’s dataSource at the end of viewDidLoad.

 self.view.addSubview(@table)
 
»@table.dataSource = self

A table’s dataSource must implement the following methods, or else the app will throw an exception:

  • tableView:cellForRowAtIndexPath:
  • tableView:numberOfRowsInSection:

numberOfRowsInSection: should return exactly what it sounds like. cellForRowAtIndexPath:’s job is to either create a new cell or recycle an offscreen one.

cellForRowAtIndexPath: uses two unfamiliar objects: indexPath is an instance of NSIndexPath, and the method is supposed to return a UITableViewCell.

There are actually a slew of methods the data source can implement, but the simplest needs only these. They should be defined in your controller and should follow this form:

 def​ tableView(tableView, ​numberOfRowsInSection: ​section)
 # return the number of rows
 end
 def​ tableView(tableView, ​cellForRowAtIndexPath: ​indexPath)
 # return the UITableViewCell for the row
 end

NSIndexPaths have section and row properties, which we use to describe the location of the row we’re setting up. UITableViewCell is the UIView subclass that actually gets displayed in our table. Its default implementation comes with some useful properties, including a textLabel and an imageView. You can subclass UITableViewCell to add a custom look and behaviors, but we can get something useful using those default subview properties.

Here’s a more complete implementation with the boilerplate filled in:

 def​ tableView(tableView, ​cellForRowAtIndexPath: ​indexPath)
  @reuseIdentifier ||= ​"CELL_IDENTIFIER"
 
  cell = tableView.dequeueReusableCellWithIdentifier(@reuseIdentifier)
  cell ||= UITableViewCell.alloc.initWithStyle(
  UITableViewCellStyleDefault,
  reuseIdentifier​:@reuseIdentifier​)
 
 # put your data in the cell
 
  cell
 end

Wait, what’s this reuseIdentifier business? Well, UITableView does its reuse magic by giving an “identifier” to each type of cell used in the table. When you grab a cell for reuse, you actually grab it from a pool of cells with the desired identifier. If you have two visually distinct types of rows, you should have two identifiers. If for some reason you had a unique identifier for every row, none of the cells could get reused because there would only ever be one cell in each pool. In our app, all of the cells will look alike, so we need only one global identifier.

We use dequeueReusableCellWithIdentifier: to grab a UITableViewCell from the identifier’s pool; if we don’t have one, then the ||= will create a new UITableViewCell with a given style and the reuseIdentifier for the cell. The style determines which properties the cell has, such as subtitles and secondary values; for now, we’re going to stick with the default.

Let’s put all this together in our AlphabetController with some actual data. In viewDidLoad, let’s initialize an array to use as the titles of our rows. Since Ruby is awesome, we can use a quick one-liner.

  @table.dataSource = self
 
» @data = (​"A"​..​"Z"​).to_a
 end

This will create an array containing all the uppercase letters of the alphabet. Nifty, huh? Now we need to fill in our two data source methods.

numberOfRowsInSection: is pretty straightforward, since we are working with pretty simple @data.

 def​ tableView(tableView, ​numberOfRowsInSection: ​section)
  @data.count
 end

We said we’re dealing with table rows, so you may be wondering about the sections. A table view is actually made out of many sections, each filled with many rows. These sections are normally separated by the semitransparent gray bars stuck to the top of a table. Right now we have only one section, so no other logic is necessary.

In cellForRowAtIndexPath:, we simply set our cell’s textLabel (an instance of UILabel) to use the corresponding string from @data.

 def​ tableView(tableView, ​cellForRowAtIndexPath: ​indexPath)
  @reuseIdentifier ||= ​"CELL_IDENTIFIER"
 
  cell = tableView.dequeueReusableCellWithIdentifier(@reuseIdentifier)
  cell ||= UITableViewCell.alloc.initWithStyle(
  UITableViewCellStyleDefault,
  reuseIdentifier​:@reuseIdentifier​)
 
  cell.textLabel.text = @data[indexPath.row]
  cell
 end

Now let’s take our app for a spin. If you weren’t familiar with the twenty-six letters of the alphabet, then now would be a great time to learn (well, except that our table just statically displays this data and isn’t very interactive). I guess we should fix that, right?

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

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