Displaying a list of records to users is one of the most common needs for any application. We see this from grids on the web, to list views on smart phones, and now the trend should continue on in smart watches. In this post, I’d like to show you how to add a table to your Apple Watch app.
The Interface Table
The easy part is to drag and drop a table control to your storyboard:
What’s not so obvious is what to do from here. The table row that is displayed is a template that will be used for all the rows and acts like a group container. In it, you will drag and drop other controls into it. For this example, let us simply add an image and a label:
Notice that the controls are placed side-to-side in the table. Again, it acts like the group control. Let’s size it right so the image takes up 25% and the label takes up 75% of the width. See the size section in the above screenshot: 0.25 of the relative to container size.
Dude, Where’s My Rows?!
How do you populate the rows with your records though? This takes a few steps. First, let’s create an outlet for our table onto our interface class. We do this by Control+dragging the table onto the class in split view. I called it currentTable and we will use this later:
Next, we will need to create a class for our table row controller. What this means is that the row needs to have a reference in code so each record can update the row components programatically.
To do this, let’s add a fresh Swift class into our project like this:
class CategoryRowView: NSObject { }
It’s just a blank class for now, but the interesting part is how we are going to wire that up to our table row. In the storyboard, show the document outline so you can see the control hierarchy of your interface, then highlight the “Table Row Controller”:
Then in the right pane, we are going to adjust the properties for the table row. In the newspaper-looking icon is the identity inspector of the table row. Here we are going to assign our “CategoryRowView” class we created to wire it together:
Also for sanity, I’ve also gave the table row a custom identifier, it can be called anything but will manifest later how it is used:
Now let’s start creating references to the row controls. We do this like any other interface item in our storyboard. Open the split view and start Control+dragging the components form the interface to the class:
I did this for the label and the image. You can call the variables anything you like:
Sweet, now we can update the row programmatically through the class properties! Our class should look like this:
class CategoryRowView: NSObject { @IBOutlet var itemLabel: WKInterfaceLabel! @IBOutlet var itemImage: WKInterfaceImage! }
Show Me the Code!
We are ready to populate the table. We do this by letting the app know how many rows this table will have, then loop through it and update the row programmatically:
override func willActivate() { // This method is called when watch view controller is about to be visible to user super.willActivate() // Initialize table currentTable.setNumberOfRows(10, withRowType: "categoryRow") // Populate the table for index in 0...9 { var row = currentTable.rowControllerAtIndex(index) as! CategoryRowView row.itemLabel.setText("Item \(index)") row.itemImage.setImageNamed("white_apple") } }
Notice I am using the same identifier name set in the properties when calling “currentTable.setNumberOfRows”. Also, I am casting the row to our class “CategoryRowView” in each iteration of the loop. This way, I can update the row outlets.
Another thing worth mentioning is I populated the table in the “willActivate” event, but this will also work in the “awakeWithContext” event. It depends on how dynamic your table will be. If the table data is constantly changing, load the table in the “willActivate” event so every time the user loads that page, it will query fresh data. Otherwise, the data will be stale and only load when the app loads the first time. It depends on the scenario really and balancing that with performance.
The Demo
Your WatchKit app should look something like this:
It populated each row using the row controller of the table. The key was to assign a class to it, create outlets to the row components, and loop through the table to assign the values.
To handle the click event for each row, override the table function in your controller with the didSelectRowAtIndex parameter:
override func table(table: WKInterfaceTable, didSelectRowAtIndex rowIndex: Int) { // Redirect to controller with unit instance self.pushControllerWithName("anotherController", context: rowIndex)) }
This event will get triggered when a table row was selected, along with the index of that row.
HAPPY CODING!!
Leave a Reply