Digging down deep to talk about the best features and advanced techniques of Focus Management with React Native.
Over the past couple of months, we’ve been building a React Native-based streaming video app for mobile, tablet, and TV devices using You.i TV’s cross-platform SDK, You.i Engine One. We’ve talked before about our experience working with You.i Engine One as a whole but today we want to dig down deep and talk about a feature that we really enjoy: the FocusManager.
When developing applications for connected TV and Smart TV platforms, it’s important to remember that user input comes from a remote.
Shocking, right?
Now that I’ve stated the obvious, how do we, as developers, handle and give proper feedback to the user when they are interacting with said remote? Enter Focus Management.
Focus management is required to make TV navigation easy for users. Without it, there is no way to let the user know if a visual element — like selecting an image or button — requires any further action. So, to build an app for a TV platform, we need a way to handle and give feedback about the focus of UI elements within the app. That’s where You.i Engine One’s FocusManager comes in.
Focus Management With You.i Engine One
When we first started developing for TV using You.i Engine One and its React Native solution, we were emulating the TV UI on a macOS app. So, we started developing features and using our pointer to interact with the UI. It wasn’t until later, when tickets about focus management started to show up, that we started testing on Android TV and used the remote control.
Now what?
You.i Engine One is built for TV
When we started paying attention to focus management, we went to You.i TV’s documentation. That’s when it became clear that You.i Engine One was developed with TV in mind. We found a full explanation on why focus management is important.
Moreover, we could rely on the same techniques and APIs for any TV platform supported by You.i Engine One. By the time of writing this piece, tvOS, Android TV, Amazon Fire TV, Roku, PlayStation 4, and Xbox One are all included.
How does it work?
The fundamentals behind focus management are the same between platforms: simple navigation and highlighting the selected element. Differences arise when implementing how the focus behaves.
By default, FocusManager only needs FocusIn and FocusOut timelines to make something focusable. This is done when using You.i Engine One’s After Effects Compositions.
After the timeline hooks up to an item, the engine will pick it up by default. Then you’ll be able to see it getting focused by using the remote control on your platform or emulator. In practice, all it will do is play a certain animation that gives the user feedback on what it is happening on the screen.
So you should always have FocusIn and FocusOut timelines for any Ref to get it focused. If there is no focus animation on them, the user will never know if the particular view is selected or not.
Advanced Techniques with FocusManager
In most cases, the default focus behavior is enough for apps built with You.i Engine One. If we need to tweak the default behavior, we can do so. There are two main ways we can interact with the focusability of elements:
Hooking up onFocus and onBlur props to focusable elements
Once we have made an element focusable, we can hook up to its focus and blur events. This is particularly helpful when we want to hook up extra logic on top of the animations to the UI elements. For example:
interface FocusManagerInterface { getTag(refOrTag: Ref): void; /** * Requests that focus be moved to the component with the given tag. * * @param refOrTag The ref or a node handle of the component. */ focus(refOrTag: Ref): void; /** * Controls whether the component with the given ref/tag is a focus root. * * @param refOrTag The ref or a node handle of the component. * @param isFocusRoot True if the component should be a focus root; * false otherwise. */ setFocusRoot(refOrTag: Ref, isFocusRoot: boolean): void; /** * Enables the focusability of the component with the given tag. * * @param refOrTag The ref or a node handle of the component. */ enableFocus(refOrTag: Ref): void; /** * Disables the focusability of the component with the given tag. * * @param refOrTag The ref or a node handle of the component. */ disableFocus(refOrTag: Ref): void; /** * Sets the focusability of the component * with the given tag to the provided value. * * @param refOrTag The ref or a node handle of the component. * @param focusable The boolean value representing whether * the component should be focusable or not. */ setFocusable(refOrTag: Ref, focusable: boolean): void; /** * Sets the focus path between the two components with * the given refs/tags in the given direction. * * @param fromRefOrTag The ref or a node handle of the component * from which the focus path originates. * @param toRefOrTag The ref or a node handle of the component * at which the focus path terminates. * @param focusDirection The direction of the focus path. * Valid directions are: * "up", "down", "right", "left", "forward" and "reverse". */ setNextFocus( fromRefOrTag: Ref, toRefOrTag: Ref, focusDirection: 'up' | 'down' | 'right' | 'left' | 'forward' | 'reverse', ): void; }
With the Focus Manager, we can do pretty cool stuff, like:
1. Autofocus:
A great user experience eases navigation and guides the user. For example, when filling out a form in an app, the focus inputs should guide where we want the user to enter information. The same notion applies to TV platforms. When a user goes to a particular screen, we want to focus on the next immediate action:
2. Overriding focusable elements:
The default focusability of elements in You.i Engine One is somewhat related to the position of elements on screen. Take a look at the following image.
A linear list of elements, focusable all in line to the right.
The expected focusability of this list when pressing right on the controller would be: A, B, C and D. As we should expect. However, look at the next two images.
The ideal focusability of elements.
Focus Manager assumes that C is the next element to A.
The image on the left shows how we want the focusability top work when pressing “right” on our remote. Yet, as the FocusManager maps elements upon their position, it assumes that the next element to A is C.
To override this behavior, we must use the setNextFocus method call like this:
3. Setting a “focus root”:
If you want to “lock” on a certain view and its children while ignoring everything around it, you can set up a focus root.
Locking focus to a certain ViewRef as the “root”
In this example, we want to lock the focusability of elements to those inside the green rectangle. We might want to do that to restrict the user from being able to focus elements outside that green area. This is especially useful when triggering modals. To set up a focus root, we simply write some code similar to the following:
4. onFocus(or onBlur)InDescendants:
This is particularly useful for lists of items when you want to apply the same onFocus or onBlur event to all elements of a list.