Guides and Sample Code


App Programming Guide for watchOS

On This Page


Use tables to display lists of data whose content changes dynamically. watchOS supports single-column tables using the WKInterfaceTable class. Displaying data in a table requires defining the layout for your data in advance and writing code to fill the table with the actual data at runtime. Specifically, you need to do the following in your Xcode project:

For each table, you can define multiple row controller types, each with a different appearance. At runtime, you specify which row types you need and in what order they should be arranged in the table. For additional information about how to configure a table, see WKInterfaceTable Class Reference.

Configuring Row Controllers

A row controller is a template for displaying a single row of data in your table. When you add a table to your interface controller scene, Xcode automatically creates an initial row controller, but you can add more. For example, you might use different row controllers for content rows, headers, and footers in your table.

To add row controllers to a table
  1. Select the table object in your storyboard file.

  2. Open the Attributes inspector.

  3. Use the Rows attribute to change the number of available row controllers.

Each row controller contains a single group element initially. To that group, you add the labels, images, and other objects that you want to include in the row. The content for labels and images in a row controller is irrelevant at design time. At runtime, you replace the content of each item when you configure the row.

Each row controller is backed by a custom class that you use to access the row’s contents. Most row controller classes contain only properties for accessing the row’s interface objects—few contain any code. However, if you add buttons or other interactive controls to a row, your row class can also include action methods for responding to user interactions with those controls.

To define a custom class for your row controller
  1. Add a new Cocoa Touch class to your WatchKit extension.

  2. Make your new class a subclass of NSObject.

  3. Add declared properties for each label, image, or control that you plan to access at runtime. Use the following format for declared properties, changing the class to match the class of the corresponding interface object:

    • @property (weak, nonatomic) IBOutlet WKInterfaceLabel* label; // Objective-C
    • @IBOutlet weak var label: WKInterfaceLabel! // Swift

Listing 13-1 shows a sample row controller class definition. In this example, the class contains outlets for an image and a label.

Listing 13-1A sample class for managing a row


  1. @interface MainRowType : NSObject
  2. @property (weak, nonatomic) IBOutlet WKInterfaceLabel* rowDescription;
  3. @property (weak, nonatomic) IBOutlet WKInterfaceImage* rowIcon;
  4. @end


  1. class MainRowType: NSObject {
  2. @IBOutlet weak var rowDescription: WKInterfaceLabel!
  3. @IBOutlet weak var rowIcon: WKInterfaceImage!
  4. }

You finish the configuration of your row controller in your storyboard file by setting its class and connecting up any outlets. You must also assign an identifier string to the row. You use that string at runtime when creating the row.

To configure a row controller in your storyboard
  1. In your storyboard file, select the row controller object.

  2. Set the row controller’s Identifier attribute to a unique value for the table.

    You use the identifier later on when creating the table’s rows. The value must be unique among the row types of the table but the actual value is at your discretion. Set this value in the Attributes inspector.

  3. Set the class of the row controller to your custom class. Set this value in the Identity inspector.

  4. Connect the labels and other elements to the outlets in your custom class.

    Connecting items in your storyboard file to your outlets binds the two together. watchOS needs this information to create a row’s interface objects at runtime.

Figure 13-1 shows an example of a row controller configured with the identifier mainRowType and the class MainRowType, which is defined in Listing 13-1. The rowDescription and rowIcon outlets in that class are connected to the image and label objects in the row.

Figure 13-1Examining a row controller in Xcode image: ../Art/table_row_definition_2x.png

Configuring the Table’s Contents at Runtime

At runtime, you add rows to a table and configure the contents of each row programmatically. Add and configure rows as part of your interface controller’s initialization process.

To create and configure the rows of a table
  1. Determine the number and type of rows you want, based on the data you want to display.

  2. Use the setRowTypes: or setNumberOfRows:withRowType: method to create the rows.

    Both methods create the rows in your interface and instantiate each row’s corresponding class in your WatchKit extension. The instantiated classes are stored in the table and can be accessed with the rowControllerAtIndex: method.

  3. Iterate over the rows using the rowControllerAtIndex: method.

  4. Use the row controller objects to configure the row contents.

The setRowTypes: and setNumberOfRows:withRowType: methods instantiate the classes associated with the corresponding row controllers. Immediately after calling one of those methods, you retrieve the newly created row controller objects and use them to configure the rows. Listing 13-2 uses some provided data to configure a label and image for a row. The data is provided by an array of custom data objects of type MyDataObject. (The MyDataObject class exposes a string and image as properties and its implementation is not shown here.) The rows themselves are instances of the custom MainRowType class, which is defined in Listing 13-1.

Listing 13-2Creating and configuring the rows of a table
  1. - (void)configureTableWithData:(NSArray*)dataObjects {
  2. [self.table setNumberOfRows:[dataObjects count] withRowType:@"mainRowType"];
  3. for (NSInteger i = 0; i < self.table.numberOfRows; i++) {
  4. MainRowType* theRow = [self.table rowControllerAtIndex:i];
  5. MyDataObject* dataObj = [dataObjects objectAtIndex:i];
  6. [theRow.rowDescription setText:dataObj.text];
  7. [theRow.rowIcon setImage:dataObj.image];
  8. }
  9. }

When configuring tables, you can improve performance by limiting the number of rows you create initially. But because table rows must all be created up front, creating large numbers of rows can adversely affect the performance of your app. The precise number of rows depends on the complexity of your data and how long it takes you to create each one, but consider keeping the total number of rows to 20 or fewer. For tables that require more rows, consider loading only a subset of rows initially and then provide the user with controls to load more rows. An even better solution is to display only the most important subset of rows. For example, you might use location data to limit the number of rows to those that are most relevant to the user’s current location.

Handling Row Selections

An interface controller is responsible for handling row selections in any tables it owns. When the user taps the row of a table, watchOS selects the row and calls the table:didSelectRowAtIndex: method of the interface controller. Use that method to perform relevant actions for your app. For example, you might display a new interface controller or update the row’s content. If you do not want a table row to be selectable, deselect the Selectable option for the corresponding row controller in your storyboard.

Tables also support item pagination. In hierarchical apps, if the table segues to a detail view whenever a row is selected, then enabling item pagination lets the user scroll between the details views without having to return to the table. Item pagination is disabled by default. For more information, see Supporting Item Pagination in WKInterfaceTable Class Reference.