The focus interaction model is a critical component of an engaging and intuitive experience on tvOS. Learn more about how it works, some additions and changes to the API, as well as tips and tricks for making your tvOS app even better.
Hi. My name is Matthew Ricketson.
I'm a software engineer working on UIKit, and today we're going
to talk about Focus Interaction on tvOS.
Last year we introduced tvOS, a great new platform
for building apps on Apple TV.
And whether you're building a brand new app for Apple TV
or looking to port an existing app over from another platform,
it's immediately obvious
that Apple TV is unlike other Apple devices.
There's no touchscreen on a TV or a mouse because these forms
of user input just don't make sense
in a living room environment.
Instead, we use this, a remote, like the Siri Remote,
in order to control Apple TV from a distance.
The Siri Remote and Apple TV have been designed together
to provide the best navigation experience.
But Apple TV also supports many other input devices,
like game controllers, Bluetooth keyboards,
and the new remote app.
And sometimes users might feel more comfortable using one
of these other input devices in order to control their Apple TV,
and tvOS helps you support all of these
through a user interaction model that we call Focus.
With Focus interaction, users indirectly control the UI
through a single focused user interface element
and they can move focus
around the screen using any input device.
Users will expect that your apps are optimized toward great
with Focus interaction.
And to help with that, UIKit provides built-in focus behavior
for all of its standard controls; however,
you may also want to customize the focus behavior in your apps
in order to take them to the next level
and provide an even better user experience.
And for that, UIKit exposes a Focus API.
Now today's talk is about that Focus API
and we split it into two parts.
First, we're going to dive into what's new
in the Focus API this year in tvOS 10.
And then in the second part of the talk, we're going to go
into an extended case study on how to build a custom,
fully focusable control completely from scratch.
This is a great demo that will show you how
to get started building unique interaction experiences
in your tvOS apps.
Now a quick note before we get started.
We're going to assume that you have at least some familiarity
with the Focus API and the basic concepts
of how Focus works on tvOS.
But even if you're new to the platform, don't worry.
You can still generally follow along, and at the end
of the talk today, we'll call out some useful resources
to help you get started with building apps for Apple TV.
So what's new in tvOS 10 and to start, we've added some new APIs
to enhance the preferred focus system.
Now as a quick review, when we talk about preferred focus,
we mean the process of determining
where Focus should show up after a Focus update.
Whenever Focus updates,
for example when your app first launches, the Focus engine needs
to know where focus should be directed to.
It does this by asking your app
where it prefers Focus gets directed.
Let's look at a specific example.
When your app first launches, the Focus engine is going to try
to figure out where Focus should show up by default,
and it's going to start by looking at the focus environment
that we're updating Focus to.
And when our app first launches, that's the window.
In this case, the Focus engine will ask the window what's its
preferred Focus view is.
The window delegates this responsibility
to its root view controller,
which could also return some other view in the hierarchy
as its preferred Focus view.
The Focus engine will then ask
that view what its preferred Focus view is
and it will continue following down this chain
until it reaches the end, which is the last focusable view
that it found as the new Focus view on screen.
Leveraging the Preferred Focus System is important
to provide a good user experience
in complex apps, especially.
So that focus is directed to the correct parts of the screen
at the appropriate times.
In tvOS 10, we're providing a new property
that allows your apps to easily express complex Focus
preferences and the name of this property is appropriately called
Preferred Focus Environments declared
in the UIFocusEnvironment protocol and it returns an array
of objects that also conform to the UIFocusEnvironment protocol.
And this property has two primary benefits.
First, by returning objects that conform to UIFocusEnvironment,
you can specify that focus be directed to any environment
on screen, not just views but also view controllers directly.
The second benefit of this property is
that it returns an array of these focus environments,
which allows you to specify an ordered list of preferences
and this enables complex, multipart-preferences
to be expressed much more concisely.
And there's a specific pattern where we can see this happening
that Preferred Focus is commonly used
in which is custom container view controllers.
Let's look at an example of that.
Say we have an app with a structure that looks like this.
We have a container view controller
with two child view controllers.
In this example, we want our container to prefer
that focus show up in the left child view controller first,
but if that's not possible, focus should be directed
to its right child view controller.
And we can express this logic very concisely using the new
Here, we override preferredFocusEnvironments
to return an array of both children with the left child
as the first item in the array.
This is great because we don't have
to check ourselves whether the left child view controller
actually contains anything focusable.
We don't have to know anything
about the contents of that controller.
We just tell the Focus engine what our preferences are
and it takes care of the hard work for us.
So if our left child view controller
in this example happened to not contain anything focusable,
the Focus engine will automatically fall back
to that right child view controller instead.
And in this example, maybe it does find something focusable.
So the preferredFocusEnvironments
property helps us to express these preferences much more
clearly and concisely,
and if the first item doesn't produce anything focusable,
we can easily fall back to the other environments in the list.
Next up, we have another enhancement
to the Preferred Focus System and this is
for more specific use case involving restoring Focus
after view controller transitions.
So before we introduce the API, let's first look at an example
to illustrate what exactly we mean by focus restoration.
Say we have an app that looks like this.
It's a simple view controller with a menu of buttons.
And our root view controller has preferred
that by default we want our first button to become focused.
It did this using the preferredFocusEnvironments
property that we just looked at.
Now suppose the user moves to the last button in the list
and selects that button
to present a detail view controller.
In that case we're interested in is
when this detail view controller gets dismissed.
After the dismissal, focus needs to be restored back
to our root view controller.
Now the user would probably expect
that focus gets restored back to the button
that was last focused, before our presentation,
which is the last button in our list.
However, if we ask our root view controller
at this time what its preferred focus environments are,
it would still direct us to that first default button.
And so instead, the Focus engine automatically remembers the last
focus item for us so that after the transition is complete,
it automatically restores focus to that last button
that the user would expect.
This is behavior that existed in tvOS 9.
While this is almost always the behavior that both the user
and often the developer expects, there are some cases
where you might want to restore focus to some other part
of your user interface.
Suppose for example that while our detail view is presented,
our app's underlying data model changed
so that the last button now represents a completely
different action or data item that it did previously.
In that case the user might not expect focus be automatically
restored back to that last item.
In that case, we want the Focus engine
to use your view controller's preferred focus environments
in order to determine where focus should be restored to.
And in tvOS 10, we're providing an API
that allows you to do just that.
It's called restoresFocusAfterTransition.
It's a Boolean property defined on UIViewController
and the way it works is very simple.
If set to true, the view controller will use the
It'll tell the Focus engine
to use its default behavior restoring focus to the last item
that was focused before the transition occurred.
If set to false, however,
then the Focus engine will instead consult your view
giving your app a chance to control
where focus gets directed to on screen.
The property is set to true by default to maintain
that previous default behavior
and it affects view controller presentation,
like we just talked about,
but other common built-in view controller transitions as well
such as navigation pops and moving focus back and forth
between your view controller and the tab bar.
We recommend that when using this property,
set it always be true or always be false so that focus updates
within your view controllers
in a consistent and predictable way.
We really hope that this property will help you solve
some of those tricky edge cases that you found in your app.
To show you how to use both of these properties
that we just talked about, I'd like to invite
up my colleague Brandon for a quick demo.
My name is Brandon and I'm here today to show you how
to take advantage of some
of the new Focus APIs available in tvOS 10.
So I've been working on a simple calendar app
and it's coming along pretty well so far.
You can view your events for a given week and you can switch
between weeks using the next and previous buttons.
Now when the app launches, we initially focus
on the previous button
when I think it would actually be a better experience
if we focused on the user's next event.
So let's go modify our app to do that.
So to direct focus, we need
to override the preferredFocusEnvironments
This returns an array of UIFocusEnvironment
for the focus engine to search to search.
We'll start by adding our upcoming events for the rest
of the week to our array.
If we have no more upcoming events, then we want focus
to default to the next button.
So we'll add that to our array as well.
Now let's run this, see if it works.
All right, so this time when the app launched, we're focused
on the super secret party I'm hosting after this session.
Now, I wonder what would happen if I canceled this party.
When an event is canceled,
it becomes grayed out and unfocusable.
So I would expect focus would just go to our next event,
which is a tvOS lab tomorrow morning.
But a good developer always tests their code.
So let's go try this out.
So I'm going to go to my AppDelegate
and cancel my not-so-secret party and rerun this app.
All right, just as we expected, when the app launched,
we're focused on the tvOS lab.
So because our first preferred focus environment was
unfocusable, the Focus engine just moved
to our next preferred focus environment.
We didn't have to modify any of our code to do that.
The Focus engine handled it for us.
Now if you want to view the details for an event,
you can select it to bring up the Details page.
From here, you can swipe left to right to go
to the next or previous event.
I received some bug reports from beta testers
that when they exit this view, the focus isn't on the event
that was just selected.
So in this case, we've selected the bash.
So if I menu out, I would expect
that we would be focused on the bash.
Instead, we're still focused on the tvOS lab,
which is our first available preferred focus environment.
So all we need to do is update our preferredFocusEnvironments
code to return our selected event
as our preferred focus environment.
So let's go back to our CalendarViewController,
and I have the selectedEventView property here which keeps track
of the selected event when we're in the Details page.
So if this property exists, I'll return it
as my first preferred focus environment.
All right, let's run this, see if it works.
Okay. So I'll select the tvOS lab, move the selection
to the bash, and then menu out,
and we're still focused on the tvOS lab.
And I'm remembering now that Matt mentioned earlier
in the session that by default,
the Focus engine does not consult your
preferredFocusEnvironments when restoring
from a view controller.
So to change that behavior,
we need to set the restoresFocusAfterTransition
property to false.
So let's go do that in our [inaudible] coder method
and rerun the app, see if it works now.
All right, we'll select our tvOS lab event again,
move our selection to the bash, and menu out.
Awesome, so now we're focused on the bash,
which is exactly what we wanted.
So by using the preferredFocusEnvironments
and restoresFocusAfterTransition properties,
we're able to provide a much better experience to our users.
Now I'd like to invite Matt back up on stage.
That was a really great demo.
Using these two new properties,
we hope that you can reduce the complexity of code in your apps
and also provide a better user experience.
Now, next up I'm really excited to announce
that we're bringing focus interaction support directly
Prior to tvOS 10, SpriteKit developers had
to manually implement focus interaction
in their SpriteKit games by manually keeping track
of their own state and handling user events directly.
But now in tvOS 10, we're extending the Focus API,
the same API you're used to in UIKit,
to also support your SpriteKit games.
But first, what would this be useful for?
Well the first and most obvious use case for focus interaction
in games is game menus.
Your users will expect that menus in games feel
and behave similar to menus in other system apps.
The Focus Interaction model also makes sense for board games,
where users navigate around a two-dimensional game board
and select items on that game board, maybe move those around.
And in fact, Focus Interaction makes sense for any type of game
where selection is a core gameplay mechanic.
And in any of this use cases,
again users will expect navigation and selection
to feel similar to the system focus behavior.
Otherwise, your app might feel weird or broken, unintuitive.
And so to help with that, we've extended the same Focus API
that you're used to in UIKit to now also work with SpriteKit.
This is a shared API, and what this means is
that SKNodes are now able to opt in to becoming focusable
and taking advantage of all
of the Focus engine's built-in capabilities.
UIKit and SpriteKit have been integrated to allow focus
to move freely between your views and your nodes
so you don't have to handle those use cases yourself.
So how exactly does this work?
First, let's review how Focus works for UIViews.
Prior to tvOS 10, UIViews were the only user interface elements
that were capable of becoming focused,
and they defined their focus ability using the
UIViews also conform to the UIFocusEnvironment protocol.
And this allows views to get focus update notifications
and also control the behavior of focus
within their subview hierarchy.
So to support SpriteKit focus, we're going to want
to bring these same capabilities to SKNodes
and we've made some changes to support that.
We're abstracting these same capabilities
out into their own protocol that we call UIFocusItem.
This protocol defines both that canBecomeFocused property
and conformance to the UIFocusEnvironment protocol.
Next, we refactored UIView and extended SKNode
to conform to this new protocol.
And this allows both UIView and SKNode
to share these exact same API while not requiring any changes
in your existing UIKit code.
We've also made additions to other parts of the Focus API
in order to take advantage of this new UIFocusItem protocol.
First, let's look at the UIFocusUpdateContext class,
which provides contextual information during focus
In order to support focus updates to SpriteKit nodes,
we're introducing two new UIFocusItem based properties:
previouslyFocusedItem and nextFocusedItem,
and these tell you which items are involved in the update.
We've made a similar change to UIScreen,
introducing a new focusedItem property that returns an object
to type UIFocusItem and this allows your app
to query what the current focus item is at any given time.
Now there's one more API change worth mentioning
but it requires a little bit more background.
In tvOS 9, the UIFocusItem protocol defined the
And prior to tvOS 10, this was how apps defined their focus
preferences, like we were talking about earlier.
But you can probably see a problem with this,
which is that it returns a type of UIView.
And if we're going to support SpriteKit, we're going to need
to define focus preferences to our SKNodes.
And so this isn't going to work.
But luckily, earlier in the talk,
we introduced this new preferredFocusEnvironments
property, which does the same thing.
But because it returns an array of UIFocusEnvironment objects
and now SKNode conforms to the UIFocusEnvironment protocol,
we can now define focus preferences to views,
view controller, SKNodes, any object that conforms
And this makes defining focus preferences
in your environments much easier and concise.
And because preferredFocusEnvironments takes
care of all the use cases for preferredFocusView does
but also provides these other benefits
that we've talked about, in tvOS 10 we're going
to deprecate preferredFocusView.
So if you're currently using preferredFocusView
in your applications, we recommend upgrading
to preferredFocusEnvironments instead.
We think this will really help.
So that's Focus Interaction support in SpriteKit.
Focus API now supports generic focus items using this new
Both UIView and SKNode conform to this protocol.
And for SpriteKit developers, you can now opt
in to supporting focus to take advantage
of all the Focus engine's capabilities.
For UIKit developers that don't use SpriteKit in their apps,
there are no changes required.
The view-based API with the one exception
of preferredFocusView is still available for your convenience.
We do encourage you, however, to start using this new API
in your code to take advantage
of more generic and future features.
For more details on exactly what you need to do in order opt
in to focus support in your SpriteKit games,
we recommend watching the What's New
in SpriteKit session happening this week, where they go
into more detail on exactly what you need to do to opt in.
So that's Focus Interaction support for SpriteKit.
And that concludes our overview of what's new in tvOS 10.
And so for the next part of our talk, we're going to go in-depth
with a case study on how
to build a fully custom, focusable control.
And for our example today,
we're going to use a five-star rating control.
Now we've already done some work and we've built a first draft
of this control and I'd like to show that to you.
We just used the normal UIButton class and presented the rating
as the title of the button.
If you select the button, it'll bump up the rating,
and if you keep selecting the button,
it'll cycle through all the available ratings.
Now this is okay.
It gives us the functionality that we need, but we really want
to take our app to the next level.
And this control, it's good but it's not very fun.
It's not very interactive.
And so we thought long and hard about this and we came
up with a new design and this is what that looks like.
That's a lot better.
Instead of just saying one-star or two-star,
we now visually indicate our rating using actual stars.
When you select the control, you go into an editing mode,
where you can use the Focus system
to change our rating between the stars.
And when we're done, we just press Select again
and we exit editing mode.
This is a great example of a control that is optimized
for focus interaction and we're going to walk you
through building this control completely
from scratch using the Focus API.
And for that, I'm going to hand things back over to Brandon.
All right, thanks, Matt.
So I have a project here
which contains our simple button control, but it doesn't look
as nice as the one Matt just showed us.
So let's just delete that.
Let's go ahead and drag in a view
to represent our new control.
We'll set up some constraints on it quickly, so 100 points
from the top, a width of 650, a height of 150,
and we'll center it horizontally in our container.
Great. Now I've already created a rating control class
to represent this control, so let's assign
that as a views class.
And let's go check out this rating control class.
We chose a subclass from UIControl
because it provides a lot of functionality to us for free.
For example, it determines if the control can become focused
or not based on its enabled state; however,
it doesn't have a focus/unfocused appearance.
We have to provide that ourselves.
So let's go do that.
We'll start by adding a corner radius and a background color
and then we'll override didUpdateFocus in context
with coordinator to provide both a focus
and unfocused appearance.
So if the next focus item is our control,
then we'll configure the focus appearance, which just consists
of scaling the view, adding a shadow,
and changing the background color.
If the previously focused item is our control,
then we'll configure the unfocused view or appearance,
which just rests all of our properties.
Now before I run this,
I also want to add motion effects to our control.
Motion effects makes your app feel more responsive
and helps the user find the focus item on screen.
So to do that, I'll create a motionEffectGroup
to add horizontal and vertical motion when I move my finger
around the touch surface.
Now I need to go back, and when our control becomes focused,
I will add the motion effect, and when it becomes unfocused,
I will remove the motion effect.
Now I make all these changes
within addCoordinatedAnimations block.
This way, our property changes are in sync
with our focus animations.
So let's see how this looks.
All right, so what we've done
so far is create a very basic control which has both a focus
and unfocused appearance and has motion effects.
So it moves when I move my finger on the touch surface.
But this is supposed to be a five-star rating control
and we don't have any stars yet.
So let's go add those.
So first we need to add some properties
to store our control state.
And then in our awakeFromNib function,
we'll add a horizontal stackView and add five stars to that.
I've already created the star classes to save some time,
but if you're following along,
I configured their focus appearance using the same
process that we just saw.
Now when the user selects the control, I want them to be able
to edit the current rating.
So I'll add a selectGestureRecognizer
to our control.
And when that's triggered, I'll toggle the editing state
and request a focus update.
When a focus update happens,
it consults our preferredFocusEnvironments.
So we'll override that property.
And if we're editing, we'll return our starViews
as our preferredFocusEnvironments.
Otherwise, we'll just return our super implementation.
Let's run this, see how it looks.
Okay, so this time when our app launched,
we have some stars in our control.
And when I select the control, focus moves to the first star
within the control; however, I'm not actually able
to move focus to the other stars.
This is because there's some space between the stars.
So when we try to move focus to the right, we actually end
up focusing on our control, which just directs focus back
to our first star using preferredFocusEnvironments.
So to fix this, we want to make sure our control is not
focusable when we're editing.
So let's override canBecomeFocused
and we'll return false if our control is currently editing.
So let's run this, see if that fixes things.
All right, that's much better.
So now I'll able to move focus between the stars.
But it's difficult to tell what the current rating is
because we don't provide any visual feedback.
So to do that, we need to update the current rating
when our focus changes.
So let's go back to our didUpdateFocus function,
and at the bottom, if we're editing,
we'll set the current rating based on the index
of the currently focused starView.
And let's go back up to our current rating property
and we'll tie that directly
to the isSelected property on the starViews.
And this property just sets the background color on the stars.
So let's see how that looks.
Okay, so now when I select the control, it's easy to tell
that the first star is selected.
And when I move focus, all the star select remain selected.
So it's easy to tell what the current rating is.
So this is pretty good but it's not great yet.
Right now I'm able to move focus outside the control,
and I don't want to be able to do
that when I'm editing the current rating.
Also, when I select the control,
we always reset the current rating back to one star.
I think it would be better if we kept the current rating
and just focused on the last selected star.
So let's go make those changes.
So we'll start by overriding the shouldUpdateFocus
to restrict focus movement.
So if we're editing and the next focus item is not one
of our star views, then we'll return false
to say no, focus can't move.
Also, in our preferredFocusEnvironments code,
we want to return our last selected star if we're editing
and the current rating is greater than zero.
So this way we don't reset the current rating every time.
Let's run this one more time, see how it looks.
Okay, now I'm no longer able
to move focus outside of the control.
And when I select a control, the current rating stays the same
and we just focus on the last selected star.
So that completes our custom control.
By utilizing the Focus APIs, we were able
to create a pretty complex yet very useful control.
Now I'd like to invite Matt back up on stage.
I rate that demo with five stars.
Okay, we chose that demo because it was a great example
of using many different parts of the Focus API in order
to create a rich focus interaction experience.
And there are just a couple parts of the implementation
that Brandon showed us that I want to highlight again
because they're important
for really creating a great user experience.
The first tip we have is to subclass UIControl.
UIControl provides a lot of basic functionality for free,
for instance, as Brandon said, defining the focusability
of your controls based on its IsEnabled property.
So when you're building custom controls,
you should also subclass from UIControl.
We also used the focus animation coordinator in order
to define our focus-related animation changes
when changing our focus state.
This is also really important for making sure
that our focus animations stay in sync
with other focus animations happening in other parts
of the user interface.
And we do this to make sure that focus naturally flows
around the screen when the user is moving
between different controls.
So when you're defining animations
for your focus appearance,
please use the focus animation coordinator.
And the last tip is to use motion effects.
Brandon showed us how to use motion effects
to create a little bit of movement
when you place your thumb on the Siri Remote and do a little bit
of movement but not enough to actually move focus.
You can use the UIMotionEffect's API
to implement this behavior very easily.
This is important because it allows your users
to get this direct connection to your app
so that they get immediate feedback as soon
as they start using the Siri Remote.
It also helps the user locate focus on the screen
so that they can sit down in front of their TV,
wiggle the remote around,
and immediately the focus element pops out.
Before we wrap us, just a few notes on testing.
The first is to point out that not all input devices have the
And this is definitely important when it comes
to testing your custom controls.
Now the good news is that if you use the Focus API
to build your custom controls, like we did in this demo,
then your control should work with any of the input devices
that we talked about earlier: Game controllers,
Bluetooth keyboards, the Siri Remote, all of those other ones.
But not all these input devices have the same capabilities
so that if your custom control relies
on some custom event handling or gesture recognition,
for example, let's say
for example you're using the Siri Remote
which generates touch events from its touch surface
and you're using that to drive some capability
within your custom control,
well if a user is using a Bluetooth keyboard,
they can't generate those touch events.
And so you'll have to do some extra work to make sure
that your control can behave correctly and be functional
for all users, in all situations.
And so definitely test custom controls
that use custom event handling with different input devices.
Try out a Bluetooth keyboard, pick up a game controller,
very important to maintaining a good user experience.
And finally, please, please test your controls
The good news here is that the Focus engine again does some
work for you.
For example, if the user wants a high-contrast user interface,
the Focus engine will helpfully put an extra ring
around the currently focused item to really make it pop
out on the screen and make it easier to see.
But for other accessibility features, such as VoiceOver,
you're going to have the do the same kinds of custom work
that you're used to on iOS to make sure
that your controls behave well on tvOS.
And so please test your controls with features like VoiceOver
to make sure that they behavior really well in all situations.
So that's our talk today.
We introduced two new API properties,
preferredFocusEnvironments and restoresFocusAfterTransition
to help manage preferred focus in your app.
We also announced the new focus interaction support
for SpriteKit and we think this will really help reduce the
complexity of your SpriteKit games.
And finally, we took you through an in-depth case study on how
to build a custom focusable control using our five star
Now before we leave today,
we'd like to mention some great online resources
to help you learn more.
Specifically, you should definitely check
out the Apple TV Tech Talks from 2016.
These cover -- these are our videos available online.
They cover a wide array of topics that are good
for people getting new to the platform but also good
if you've had some experience building for tvOS already.
Specifically, there's one section, Focus-Driven Interfaces
with UIKit that goes more in-depth into some
of the more basic aspects of the Focus API,
some of the basic concepts, an in-depth overview of some
of the parts of the API that we didn't cover today.
It talks about some best practices and also tools
and debugging support, which we have available to help you
with building your apps.
You can find these talks here at this URL.
And for more information on today's session,
you can go to this URL to get some helpful resources.
There are some other related sessions this week
that you should check out.
If you're building games with SpriteKit, definitely go
to the What's New in SpriteKit session, where we also talk
about how to specifically opt in your SpriteKit games
for focus support and then also check out the What's New in tvOS
in Mastering UIKit on tvOS sessions
to really take your UIKit apps to the next level.
And of course, have a great conference.
Thank you for coming today.
Looking for something specific? Enter a topic above and jump straight to the good stuff.
An error occurred when submitting your query. Please check your Internet connection and try again.