top of page

Developing for Apple TV, Part II

In the last post we covered the main differences developing for tvOS, how to set up your project, and some of the details related to managing the focus. In this post, we’ll dive more into focus management using focus guides. UIFocusGuide inherits from UILayoutGuide and enables finer control over the system focus engine.

Using focus guides The system focus engine typically only searches horizontally or vertically for the next view to put in focus when the user swipes horizontally or vertically on the remote. This was inadequate for navigating between tags on a photo. Tapping on a product tag in a photo on our iOS and Android apps provides information on the tagged product. We wanted to have a similar experience on the Apple TV, but the user can’t tap on a tag. Instead, the user needs to navigate the focus from tag to tag. The default focus engine behavior wasn’t able to handle the case of navigating between arbitrarily placed views. For this scenario, tvOS has focus guides. You can place focus guides directly horizontally or vertically from the focused view to tell the focus engine what view you want focused next if the user navigates in a particular direction. This problem is similar to placing pins on a map and navigating the focus between them. So let’s see how we solved this:

First, lets make a simple TV project. Make a single view TV app project in xcode where we’ll randomly place pins on the view. To do this, we first need to make a pin view. Our pin view is going to be a simple subclass of UIImageView and it will show a pin image. Here is our pin view class:


    class PinView: UIImageView { 
      override init(image: UIImage?) {
       super.init(image: image)   
       adjustsImageWhenAncestorFocused = true // 1
       userInteractionEnabled = true // 2
      }
        
      required init?(coder aDecoder: NSCoder) { // 3
       fatalError(“init(coder:) has not been implemented")
      }

      override func canBecomeFocused() -> Bool { // 4
        return true
      }
    }

Explanation:

1. We set a property on UIImageView that makes it adjust the image or one of its ancestors when it’s in focus. The UIImageView will zoom the photo and make it respond to the user slightly rubbing his finger on the remote.

2. We enable user interaction, which is off by default on UIImageView. Without this, the view will not get the focus.

3. The required initializer. Without this, Xcode is extremely unhappy…

4. We override canBecomeFocused to allow this view to become focused. The default is false.

Now in our view controller we will simply add a few random pins to the view on viewDidLoad as such:


    class ViewController: UIViewController {  
     var pins = [UIView]()
     
     override func viewDidLoad() {
       super.viewDidLoad()
       
       // add pins
       for _ in 0 ..< 10 {
        let pin = PinView(image: UIImage(named: