I recently did some consulting work for a new startup here in San Francisco. I built for them a couple of quite complex UI animations for an Apple TV app. Doing so, I was able to work very closely with tvOS and its very interesting interaction mechanism: the Focus Engine. I am quite fascinated by this new interaction model. After spending almost 8 years with touch-based devices, this new toy brings to my work some fresh air. I am going to demonstrate you how the Focus Engine works using the Watch OS UI example I built it here some month ago. Please, review that example if you haven’t done it so far, because I am going to use it as it is without going again into details. There is some math involved, but complex UI animations sometimes require some math.

Looking at the current UIs available on the Apple Store, I think you will agree that they look very similar. Look at Hulu, HBO, Netflix. Everything looks the same. Movies and TV shows are always represented in a grid (UICollectionView) and you can navigate them swiping left, right, up and down. Now, today this looks a novelty (or quite), but within a million tvOS apps, this will start to be very boring.

Few months ago, I published this article showing how to build the Apple Watch UI. Let’s bring it to the Apple TV.

If you simply port the source code of that example to the Apple TV, it won’t work as you expect. The reason mainly resides in the new interaction mechanism provided by tvOS.

tvOS Focus Engine

On the iPhone, iPod and iPad users interact with a touch-based screens. Instead, on the Apple TV users interact with the device through a remote control. Each interaction on the remote is mapped to the Apple TV screen by the Focus Engine. In this model, a view is focused when that view is the target of any user action. Users can move the focus to any UI view that is focusable. For example, if a button is focused, then the user can press the Select button on the remote to trigger the action associated to that button.

The focus engine just works. You can request programmatic focus updates, but you cannot set the focus explicitly. Some UI elements (such as a UIButton) are focusable by default. Other UI elements (such as a UIView) can be made become focusable.

Only the focus engine can update the focus (not you). The focus engine provides the UIFocusEnvironment protocol to communicate with your app. UIView, UIWindow, UIViewController and UIPresentationController conform to the UIFocusEnvironment protocol. When overriding the methods and properties provided by this protocol, you are able to interact with the focus engine.

Collection View

Since Apple expected collection views (and table views) to be used often on Apple TV, new methods have been added to the UICollectionViewDelegate (and UITableViewDelegate) protocol. These methods are specific to the focus engine:

The first method collectionView:canFocusItemAtIndexPath: needs to return a boolean specifying if the cell at the given index path can be focused.

The second method indexPathForPreferredFocusedViewInCollectionView: needs to return the index path of the cell that should be the preferred focus cell. This is the first cell that the focus engine focuses when the collection view appears on screen.

The collectionView:shouldUpdateFocusInContext: needs to return a boolean, specifying if the focus change should occur or not. When the user points the focus to a collection view cell, the focus engine sends collectionView:shouldUpdateFocusInContext: to the collection view delegate object. If this method returns false, then the focus does not change and the current cell remains focused. The second argument of this method is an instance of UICollectionViewFocusUpdateContext. This object provides the previouslyFocusedIndexPath and nextFocusedIndexPath properties. Using these two properties you can interact with the focus engine. For example, if the context is trying to go from the first cell to the second cell, then you can decide to return true. In this way, the focus will not change.

The fourth method collectionView:didUpdateFocusInContext:withAnimationCoordinator: is executed after the focus has changed. Here, the third argument provides an instance of type UIFocusAnimationCoordinator. During a focus update, the focus engine animates the previously focused view to an unfocused state, and the next focused view to a focused state. The animation coordinator allows other views to coordinate their animations along with the primary animations of the previously or next focused views. This class provides only a single method (addCoordinatedAnimations:completion:) that you can use to specify the animations to coordinate with the active focus animation.

Building an example

Let’s see how to apply this knowledge to our Apple Watch UI example. First, if you built that example for iOS and simply recompile it for the tvOS, you will see the UI as you expected, but the interaction is quite broken. In the following picture, you can see what happens.

tvOS Example 1

The bottom-right item (dark yellow) is the currently focused item. Now, if you try to swipe to the right (or to the bottom), the collection view will not move and the focus will not change. The problem comes from the fact that the Focus Engine does not see any additional cell items on the righthand side (and at the bottom) of the focused view. In this solution, the user needs first to focus cells on the lefthand side and then, try again to move faster to the right. So, the interaction is quite broken.

In addition to that, in a real app with this UI, you don’t want to focus cell items close to the view edges. Instead, you want to focus the cell in the middle of the view, because they offer a larger surface that can be filled with some content (TV show or movie artworks, for example).

Let’s fix this interaction. First, I want to be able to see clearly which cell is currently focus. In the original post, I did not add any interaction feedback. There, it made sense, since the user is touching the UI and she can easily associate the touch to the collection view cell. Here, a visual feedback is very important, since users control the UI from a remote and they cannot associate easily the finger position on the remote and the focused view on the TV. So, we need to provide a feedback.

I am going to use one of the methods added to the UICollectionViewDelegate that we analyzed in the previous section. I add to the collection view delegate, the collectionView:didUpdateFocusInContext:withAnimationCoordinator: and use the context to get the previouslyFocusedIndexPath and the nextFocusedIndexPath.

The second if-statement checks if the next focused cell exists. If it exists, I am adding a shadow to the content view and some border. The first if-statement, check if a previously focused view exists and it does, I remove the shadow and the border.

Once you add this code, you get what you expected. The following picture shows the result.

Focus Engine Example

As you can see the focused view remains centered in the screen. Now, if you swipe to the left or the right the next cell will move to the center.

Conclusion

tvOS introduces a new interaction mechanism based on the Focus Engine. Play with it and try to build your UI for this new OS. It’s a lot of fun.

If you liked this post and you need help with iOS, tvOS or OS X, do not hesitate to contact us. We can either build apps or frameworks for you or even teach you or your team to build apps.

Geppy

Geppy Parziale (@geppyp) is cofounder of InvasiveCode (@invasivecode). He has developed iOS applications and taught iOS development since 2008. He worked at Apple as iOS and OS X Engineer in the Core Recognition team. He has developed several iOS and OS X apps and frameworks for Apple, and many of his development projects are top-grossing iOS apps that are featured in the App Store.

iOS Consulting | INVASIVECODE

iOS Training | INVASIVECODE