Written by Maciej Banasiewicz
Published February 16, 2016

Introduction to tvOS – the new focus engine

I want to tell you how does the new focus engine work and show you the list of things that I found interesting during the development of native tvOS application for VGTV. So, welcome to tvOS!

It is finally here! (Well in fact, it has been around for a couple of months now)

The new Apple TV, along with the new tvOS system, was announced on September 9, 2015. This new exciting platform brings thousands of new possibilities allowing to deliver your content, and thanks to the fact that it is based on iOS system, time required to bring your ideas to live is relatively short, if you’ve had any prior iOS development experience.

However, as with everything in life, the devil is in the details. This little box brings philosophical changes along, and here is the most important thing to note: applications that you are going to create no longer run on a hand held device, and the user interacts with your content indirectly. 

What is important?

  • You have to take parental controls under consideration (if you have not done it before). In most cases Apple TV is located in the living room, connected to a tv set that is easily accessible to every family member. Moreover it starts surprisingly fast and has no “off” mode, so it is always ready to act.
  • You still have to watch out for performance but battery efficiency problem doesn’t exist. I discovered that creating applications for devices that are connected to a power socket in the wall is a great thing. You can always utilize 100% of the resources available and no one will mind.
    However, as I mentioned before, the devil is in the details. The applications remain in the memory for much longer then in iOS. Also, the device has no “off” mode. So in practice this means that an application that was opened 2 weeks ago remains in the same state as left by the user. This forces the developers to create the code that will handle updating content.
  • You need to make sure your content is readable from a distance. It is so obvious that people tend to forget about it.
  • Try to keep user input to minimum. There is a good reason why even the tvOS simulator can be controlled only by a remote controller on a remote simulator. It’s difficult to write the text using it!
  • You need to learn how to control / alter focus. The focus engine is a very intelligent tool and in most cases it will work out of the box. Most of visual elements that the UIKit provides are already focus friendly, however, if you go beyond a simple table / collection view you will need to understand how it works.

If you keep in mind the points that I mentioned above, you will avoid unpleasant discoveries during your development. Now lets discuss this new toy called focus engine.

We look for iOS developers in Krakow and Gdansk!

What is focus engine?

As always the best answer can be found in the documentation:

The system within UIKit that controls focus and focus movement is called the focus engine. A user can control focus through remotes (of varying types), game controllers, the simulator, and so forth … – Apple

The definition is already straightforward but to put it even simpler, it’s a part of UIKit framework, which maps user input into focus changes / actions.

It introduces four new classes that we must get familiar with, if we want to interact with it. But do not worry! As always, Apple follows the same patterns everywhere, so it super easy to learn them!

Here is the list:

  • UIFocusHeading – tells you where focus is heading, it is a property of update context
  • UIFocusUpdateContext – describes focus transition between views, objects of this class are passed to didUpdateFocusInContext callback
  • UIFocusEnvironment – this is the most important protocolit allows you to communicate with the system and alter focus engine behavior. Each focus environment represents a separate branch of a tree that focus engine traverses in order to pick an item that is going to get focused. By default it is implemented by classes that are responsible for managing views: UIView, UIViewController, UIWindow, UIPresentationController. Focus environment can override preferred focused view – this comes in handy when you want to control initial focused view, which is by default the closest one to the top left corner (or top right corner in RTL environment) or when you update focus programmatically. It also allows you to block and request focus updates programmatically. To decide if a focus change is allowed you can override shouldUpdateFocusInContext, and to request focus update using code you can use setNeedsFocusUpdate and updateFocusIfNeeded combination.
  • UIFocusGuide – UIFocusGuide is a subclass of UILayoutGuide. If you are not familiar with what UILayoutGuide is, let me describe it to you briefly. Layout guides are objects that take part in the layout process but are not visible to the user, therefore focus guides are objects that take part in the focus and layout process but are not visible to the user. Focus guides act as a link in focus and they allow you to specify target view which should get in focus by preferredFocusedView variable.

How does changing focus work?

I would like to start this part by pointing out that a bare UIView is not focusable by default, and in order to make it focusable we need to override the canBecomeFocused method. But that’s not enough! Focus engine looks at the user interface the same way we do (well not completely) and picks the next item that should get focus based on the following criteria:

  1. canBecomeFocused method returns true
  2. It is visible, meaning alpha is non zero and hidden flag is set to false
  3. It is not covered by other UI elements
  4. User interaction is enabled
  5. It’s located within the area of search

If conditions above are true, focus is changed.

The focus engine is only capable of searching for next focused item diagonally, so for example if you move your finger to the left on your remote’s trackpad, the system is going to look for the next item in the area that is located to the left of the currently focused item which has equal height as current focused item and expands to the end of the screen.

The image below depicts the idea:

 

Example that depicts how area of search looks like

So, if we find ourselves in a situation like this:

Here the red view is in focus and we would like to move focus to the the button, however, it won’t happen because of the way the area of search looks like. So how do we solve this? We need to use UIFocusGuide! Below is the sample code that describes how to create it.

// button variable is a reference to a button
let focusGuide = UIFocusGuide()
focusGuide.preferredFocusedView = button
view.addLayoutGuide(focusGuide)
[focusGuide.topAnchor.constraintEqualToAnchor(leftBox.topAnchor),
 focusGuide.bottomAnchor.constraintEqualToAnchor(leftBox.bottomAnchor),
 focusGuide.rightAnchor.constraintEqualToAnchor(leftBox.leftAnchor, constant: -100.0),
 focusGuide.widthAnchor.constraintEqualToConstant(250.0)
].forEach { $0.active = true }

And this is how focus guide created above looks like to focus engine:

 

UIFocusGuide in focus engine debugger

This image was created by a debugging tool provided by the system (checkout tips and tricks section located below to see how you can use it). Please note that focus guides have blue color and views are marked using violet, as you can see below.

 

Focusable UIView in focus engine debugger

This concludes my brief introduction into focus engine. Remember that Apple provides with great documentation and sample code covering the subject. But at this point you should know enough to make your own custom focus environment.

Focus engine tips & tricks

  • Do not allow elements that do not provide any action to become focused, it will confuse your users!
  • If you want to perform an animation triggered by focus change make sure that you add it to  UIFocusAnimationCoordinator in didUpdateFocusInContext call. UIFocusAnimationCoordinator adjusts animations according to pace with which user changes focusable items.
  • If you want to check why one of your views / focus guides doesn’t get focused you may ask the system by invoking _whyIsThisViewNotFocusable method on it. (This works only via lldb)
  • If you want to nest UICollectionView inside of another UICollectionView, the outer one should return false / NO in canFocusItemAtIndexPath delegate call
  • As I mentioned before instances of UIView are not focusable by default, however, some of the UIView subclasses provided by UIKit framework have predefined animations. For example UIImageView class has adjustsImageWhenAncestorFocused property (by default is set to false) that unlocks a very subtle and elegant animation.
  • There is also a very simple way to visualize how UIFocusEngine determined the next focusable item (based on user interaction). In order to achieve this just put a breakpoint in didUpdateFocusInContext.

 

After breakpoint gets triggered, select the context and hit space

 

If breakpoint was triggered due to user interaction you should see an image similar to one above!

 

Things you should read:

@_banasiewicz

Written by Maciej Banasiewicz
Published February 16, 2016