tvOS Tutorial: Using TVML Templates

In this tvOS tutorial, you’ll learn how to use TVML templates and templating engines to make great-looking user interfaces. By Chris Belanger.

Leave a rating/review
Save for later
Share
You are currently viewing page 3 of 4 of this article. Click here to view the first page.

Using the Mustache.js Templates

Open teamwork.json and take a look at the data you’ll use to populate the video page. There’s quite a lot of data, but it’s a standard JSON object and fairly easy to understand. You should spot some fields such as title, presenter and duration that you’ve already hard-coded into video.tvml. You’re now going to swap these out.

Open video.tvml and find the title tag that contains Ray Wenderlich. Replace the name with {{presenter}}, so that the first section now looks like this:

<info>
  <header>
    <title>Presenter</title>
  </header>
  <text>{{presenter}}</text>
</info>

The syntax for Mustache.js is really simple; it will replace {{presenter}} with the value of presenter in the data object supplied to it.

Now that you’ve got the hang of that, you can replace the following content with the respective template tags:

  • Teamwork{{title}}
  • 17m 54s{{duration}}
  • Inspiration{{category}}
  • 2015{{year}}
  • resource://nrresource://{{rating}}

Build and run; you shouldn’t see any difference, which is exactly what you want. The page is now data-driven, and even better, you didn’t break anything. Bonus! :]

There are still some parts of the template you haven’t touched: closed-captions, HD and tags. These use some slightly more advanced parts of the Mustache.js templating engine.

Template Sections

Remove the three tags in the Tags section and add the following in their place:

{{#tags}}
  <text>{{.}}</text>
{{/tags}}

This new syntax defines a template section. Look at teamwork.json and you’ll see that tags is an array of strings. The Mustache.js syntax here loops through the array, with {{.}} rendering the content of the current index.

Finally, you need to handle the two Boolean badges. Replace the cc and hd badges with the following:

{{#closed-captions}}
  <badge src="resource://cc" />
{{/closed-captions}}
{{#hd}}
  <badge src="resource://hd" />
{{/hd}}

Once again you’re using sections, but this time they’re structured like an if statement. If the specified property exists and has a true value, then render this section; otherwise, ignore it.

Build and run again; check out your newly templated video page.

To confirm that the data injection is actually working, open main.js and change the data file loaded in loadInitialDocument() from teamwork.json to identity.json. Build and run again to see the data change.

You can now see details of Vicki’s talk on identity — sweet!

on

Filling out the TVML Template

The video page is still looking a little barren. It’s time to double-down on adding some content.

Open video.tvml and add the following inside the <stack>, just below the existing <row>:

<description allowsZooming="true"
  moreLabel="more">{{description}}</description>
<text>{{language}}</text>
<row>
  <buttonLockup type="play">
    <badge src="resource://button-play" />
    <title>Play</title>
  </buttonLockup>
  <buttonLockup type="buy">
    <text>$9.99</text>
    <title>Buy</title>
  </buttonLockup>
</row>

Once again, this introduces some new TVML elements:

  • <description>: Displays a larger amount of text that’s used to describe content. If the text is too long for the display area then a label will be displayed with a title defined by the moreLabel attribute.
  • &ltbuttonLockup>: A lockup is a class of element that joins its children together as a single element. A button lockup can contain text and a badge and will appear as a button.

Remember that these elements are all contained within a <stack>, so they’ll appear on top of each other.

Before checking your work, you need to add one more element to the top banner. Add the following line immediately after the </stack> closing tag:

<heroImg src="{{images.hero}}" />

A heroImg element is a large image that defines the content of this document. It appears inside the <banner> and tvOS uses it to define the blurred page background.

Build and run to see the completed top banner:

It’s starting to look really cool – But wait! You may now have noticed that the text color has changed from black to white. How did that happen?

tvOS has decided that because the background image you specified is darker than a certain threshold, it needed to increase the contrast of your text, so it changed the default text color.

Update the <productTemplate> tag to match the following:

<productTemplate theme="light">

Build and run to see the difference:

The visual effect on the background has changed along with the foreground font colors. You can change the theme attribute to dark if you wish to force the previous look if you prefer it.

Note: The default behavior has changed since the previous version of tvOS, so you may need to explicitly define the theme to get the effect you’re used to. Also, please don’t get this behavior confused with the new dark mode introduced by Apple in tvOS 10. That will be covered in more detail later in the book.

Adding Shelves

The remainder of the productTemplate is made up of “shelves”. A shelf is a horizontal section of the page with content elements scrolling on and off the screen.

Add the following below the closing </banner> tag, towards the bottom of video.tvml:

<shelf>
  <header>
    <title>You might also like</title>
  </header>
  <section>
    {{#recommendations}}
      <lockup>
        <img src="{{image}}" width="402" height="226" />
        <title>{{title}}</title>
      </lockup>
    {{/recommendations}}
  </section>
</shelf>

This shelf displays a set of other videos that the user might enjoy as defined in the recommendations property of the data model. Each recommendation has an image and a title, each of which you use in the code above.

There are two other elements introduced in this code segment:

  • <section>: Defines a set of related content that should all be laid out together. A section can contain a title and multiple lockup elements.
  • <lockup>: You saw <buttonLockup> before; lockup is a more general type of lockup. It provides layout for an image, a title, a badge and a description.

Now add another shelf below the one you just created:

<shelf>
  <header>
    <title>Production</title>
  </header>
  <section>
    {{#people}}
      <monogramLockup>
        <monogram firstName="{{firstname}}"
          lastName="{{lastname}}"/>
        <title>{{firstname}} {{lastname}}</title>
        <subtitle>{{role}}</subtitle>
      </monogramLockup>
    {{/people}}
   </section>
</shelf>

This shelf displays a list of people associated with the production; it’s stored in the people property in the data model.

This introduces the and elements, which let you represent a person when an avatar isn’t available. Like the other lockup elements, a monogram lockup simply locks its content together.

A monogram has firstName and lastName attributes, from which it generates a monogram (initials) and places it in a large circle.

Build and run to see how your shelves are taking shape. You will have to scroll down to reveal the lower shelf:

Take a look at the description for the “Identity” talk. Notice that the more label has appeared, because there is too much text to display in the available space. Navigate to the label and you’ll see you can focus on the description and press select to trigger an action. This action doesn’t currently do anything, but wouldn’t it be nice if it would display the full text?

Time for another TVML template.

Chris Belanger

Contributors

Chris Belanger

Author

Over 300 content creators. Join our team.