All Blog Posts

WatchKit Table Views - A Beginner's Guide

Like the majority of controls in WatchKit, Table views are very different compared to UIKit. I see the table framework Apple have created for the watch as both good and bad. They have removed all the datasource and delegate methods you may be used to, well mostly. This really is a step in the right direction because as you may know table datasource and delegates are a massive pain and can turn into a horror show if not managed properly.

3 SIDED CUBE
10 Min Read

At 3 SIDED CUBE we hated table datasource and delegates so much we wrote our own table view framework to get rid of them, find it here: Thunder Table (p.s. it calculates all your cell heights for you!).

Creating your first WatchKit Interface table:

If you are wondering how to create a table then this is the section for you. However if you prefer to read code rather than English here is the Final Project. The end result will be a fully working table view on the watch of all my favourite IT Crowd quotes that you can click through to view. You should end up with something like this:

Step 1:

Set Up a new blank Xcode project with a watch extension. If you don’t already know how to do this see our Getting started with WatchKit tutorial.

Step 2:

Let’s build the interface for our app! Drag a table on to your main interface in the watch storyboard. Interface builder will add the first row in the table for you, each type of row is represented as a group.

Step 3:

Now we need to build the basic ui of our row. Drag a group into the row – this will be our profile image – and two labels, one will be the name and the other will be the quote. As this tutorial is not about groups and ui layout I will let you decide how you want your row to look and how your views should be laid out, the best way is to play around and see what looks best. My row ended up looking like this as I felt this was the clearest way to layout the data:

Step 4:

In WatchKit when you create a table row it needs a model to represent it so you can access the properties on it. So let’s create a new class to represent of our row. Go into the watch extension folder and go File > New File > iOS > Cocoa Touch Class, the new class should subclassed from NSObject and can be named whatever you like, I called mine ICBQuoteTableRow. You will also need to import WatchKit into the header of your new class, add this line of code below your headers:

@import WatchKit:

Step 5:

We now need to link our row and the views in it to our model. Go back into interface builder and select the row controller. Go into Identity Inspector on the right and change the row controller class to the class you created.

Step 6:

Now we need to link up our views to our model using outlets. Ctrl + Click on the views we added to our row and drag them across to the header of your row class. You should have an outlet for the image group, the name label and the quote label.

Step 7:

Before we start creating our rows we need some data to work from. I created a class that returns a NSDictionary of the data we will need to generate our tables, download the class here and add them to your watch extension. Also make sure the new class is in your compile sources (it should be in there automatically but if it fails to build that will be why!).

Step 8:

To be able to refer to our custom row in our interface controller we need to give it an identifier. Go back into interface builder and click on the row controller. Go to the attributes inspector on the right and change the rows identifier to “QuoteTableRow” and Selectable is checked.

Step 9:

We also need to create an outlet to link our table with our Interface Controller. CTRL + Click on the table and drag to your interface controller file. For simplicity just name it something like “table”.

Step 10:

At this point you may be wondering if we are ever going to do any coding, so it’s about time we get into the code. Go into yourInterfaceController.h file, at this point it’s looking a bit bare with just Apples stubbed out methods. Firstly we need to import our quote data and row model:

#import “ICBQuoteTableRow.h” #import “ICBQuoteSource.h”

Then we need to load it into an array, create a public array called “quotes”:

#import “ICBQuoteTableRow.h” #import “ICBQuoteSource.h”

And then load the data into the quote array:

self.quotes = [ICBQuoteSource quoteDictionary];

We now have an array of dictionaries that represent our quotes, each quote dictionary looks like this:

{ “characterImage”: “moss”, “characterName”: “Moss”, “quote”: “I came here to drink milk and kick ass… and I’ve just finished my milk.” }

So each quote object has a character image, character name and the quote.

Step 11:

Now we need to generate our table rows based on our array of quotes. So set the number of rows to the count of the quote array:

[self.table setNumberOfRows:self.quotes.count withRowType:@“QuoteTableRow”];

This will load 5 of our custom rows into our table at this point time they will have the stubbed data from interface builder. At this point you might be wondering how we access the rows we’ve created and set the data on them, well here how! So to access the rows we do this:

for  (NSDictionary *quote in self.quotes)  { ICBQuoteTableRow *quoteRow = [self.table rowControllerAtIndex:[self.quotes indexOfObject:quote]]; [quoteRow.characterPicture setBackgroundImageNamed:quote[@“characterImage”]]; [quoteRow.characterName setText:quote[@“characterName”]]; [quoteRow.quoteLabel setText:quote[@“quote”]]; }

So this loops through each quote and pulls our row objects of the table, we then set the properties on the rows. For the images of the faces to load into the tables you will need to add the character pictures into your watch app XCAssets folder and name them based on the quote data. Eg. Moss’ character picture is saved as “moss”.

If you run the watch project now you should have a full table view with all the quote data displayed in the rows. Hopefully it will look pretty similar to this:

Step 12:

At the moment our table rows do nothing when selected this is because we haven’t implemented the table selection delegate, so let’s add it! In your InterfaceController.h file add this method:

– (void)table:(WKInterfaceTable *)table didSelectRowAtIndex:(NSInteger)rowIndex { }

As you can see this row selection delegate is very similar to the method we use in UIKit. When the user selects the cell we want to push them to a new screen that displays the full quote. Add this line to your selection delegate:

[self pushControllerWithName:@“QuoteDetailInterfaceController” context:[self.quotes objectAtIndex:rowIndex]];

Here we are pushing a new interface controller (which we will create in a second) and parsing the selected quote through. As you can see here we never create instances of other interface controllers like we do in UIKit instead we use an identifier to reference them which is set in interface builder.

Step 13:

Let’s switch back into interface builder and make up our detail interface controller! Drag a new interface controller object from the object library on the bottom right. Add an image and 2 labels, lay them out how you wish. Click on the new interface controller and go into the attributes inspector on the right and change identifier to “QuoteDetailInterfaceController”.  Make sure that the second label’s number of lines is set to 0 otherwise the text will not wrap correctly.

Step 14:

Next we need to create a new interface controller class to represent our detail view. Add a new file in your watch extension and call it “QuoteDetailIntefaceController”. Make sure its a subclass of WKInterfaceContoller!

Step 15:

Now thats we’ve created a class to represent our detail controller we need to link it up to the interface controller we created in our storyboard. Go back into interface builder and click on the detail interface controller and go into to identity inspector on the right. Change the controllers class to our new interface class.

Step 16:

Now we want to connect our labels to our code so create outlets from the items to your detail interface controller.

Step 17:

Go back into to your QuoteDetailInterfaceController.m. The first thing we need to do is get the quote that has been parsed to the controller in the context. Create a NSDictionary property at the top of the file:

@interface QuoteDetailInterfaceController() @property (nonatomic, strong) NSDictionary *quote; @end

Now lets pull our quote of the parsed context. All objects are parsed into to an interface controller using an id as context. Set the quote property to context in the awake method:

– (void)awakeWithContext:(id)context { [super awakeWithContext:context]; self.quote = context; }

We now have a reference to the quote that was parsed in from the previous controller. Next let’s set our image and our labels to the correct values:

– (void)willActivate { [super willActivate]; [self.characterImage setBackgroundImageNamed:self.quote[@“characterImage”]]; [self.characterNameLabel setText:self.quote[@“characterName”]]; [self.quoteLabel setText:self.quote[@“quote”]]; }

If you run the project now you should be able to click on a cell and it will take you into another interface that shows the full quote. The new interface will scroll if the quote goes off the screen. If your full quote label isn’t wrapping make sure you set the labels number of lines to 0!

Conclusion:

After completing this tutorial you should have a pretty good understanding of how to create tables and push to detail interfaces! You might have noticed that making tables in WatchKit doesn’t actually require much code and most of the work is done in interface builder.

In case you missed it earlier you can download the complete project that also has all the assets in here: Final Project

Published on 13 March 2015, last updated on 9 April 2019