En iOS, los usuarios normalmente interactúan con sus aplicaciones a través de la pantalla táctil del dispositivo. En tvOS, sin embargo, la interacción del usuario se maneja moviendo la corriente atención entre vistas en la pantalla.
Afortunadamente, las implementaciones de tvOS de las API de UIKit manejan el cambio de enfoque entre las vistas automáticamente. Si bien este sistema integrado funciona muy bien, para diseños y / o propósitos de vista específicos, puede ser necesario a veces controlar manualmente el motor de enfoque..
En este tutorial, analizamos en profundidad el motor de enfoque de tvOS. Aprendes cómo funciona y cómo controlarlo como quieras..
Este tutorial requiere que estés ejecutando Xcode 7.3 o superior con el último SDK de tvOS 9.2. Si desea seguir adelante, también necesita descargar el proyecto de inicio de GitHub.
El propósito del motor de enfoque de tvOS es ayudar a los desarrolladores a concentrarse en el contenido único de su propia aplicación en lugar de reimplementar los comportamientos básicos de navegación. Esto significa que, si bien muchos usuarios utilizarán el control remoto Siri de Apple TV, el motor de enfoque admite automáticamente todos los dispositivos de entrada actuales y futuros de Apple TV.
Esto significa que, como desarrollador, no tiene que preocuparse por cómo un usuario interactúa con su aplicación. Otro objetivo importante del motor de enfoque es crear una experiencia de usuario consistente entre las aplicaciones. Debido a esto, no hay una API que permita que una aplicación mueva el foco.
Cuando el usuario interactúa con el control remoto del Apple TV al deslizar la superficie táctil del vidrio en una dirección particular, el motor de enfoque busca una posible vista enfocable en esa dirección y, si lo encuentra, mueve el enfoque a esa vista. Si no se encuentra una vista enfocable, el foco permanece donde está actualmente.
Además de mover el enfoque en una dirección particular, el motor de enfoque también maneja varios otros comportamientos más avanzados, como:
Al determinar dónde debe moverse el enfoque en una aplicación, el motor de enfoque toma una imagen interna de la interfaz actual de su aplicación y resalta todos los elementos visibles que son enfocables. Esto significa que cualquier vista oculta, incluidas las vistas con un valor alfa de 0, no se puede enfocar. Esto también significa que, para cualquier vista que esté oculta por otra vista, el motor de enfoque solo considera la parte visible..
Si el motor de enfoque encuentra una vista a la que puede mover el foco, notifica a los objetos que se ajustan a la UIFocusEnvironment
Protocolo que están involucrados con el cambio. Las clases UIKit que se ajustan a la UIFocusEnvironment
protocolo son UIWindow
, UIViewController
, Vista
, y UIPresentationController
. El motor de enfoque llama al shouldUpdateFocusInContext (_ :)
método de todos los objetos del entorno de enfoque que contienen la vista enfocada actualmente o la vista a la que se está moviendo el enfoque. Si alguno de estos métodos llama devuelve falso
, el enfoque no se cambia.
los UIFocusEnvironment
protocolo representa un objeto que se conoce como una entorno de enfoque. El protocolo define un preferredFocusView
propiedad que especifica dónde debe moverse el foco si el entorno actual se enfoca en sí mismo.
Por ejemplo, un UIViewController
por defecto del objeto preferredFocusView
Es su vista de raíz. Como cada Vista
objeto también puede especificar su propia vista de enfoque preferida, una cadena de enfoque preferida se puede crear. El motor de enfoque de tvOS sigue esta cadena hasta que un objeto particular regrese yo
o nulo
de su preferredFocusView
propiedad. Al usar estas propiedades, puede redirigir el enfoque a través de la interfaz de usuario y también especificar qué vista debe enfocarse primero cuando aparece un controlador de vista en la pantalla.
Es importante tener en cuenta que, si no cambia ninguno de los preferredFocusView
propiedades de sus vistas y controladores de vista, el enfoque por motor predeterminado enfoca la vista más cercana a la esquina superior izquierda de la pantalla.
Una actualización de enfoque se produce cuando uno de los tres eventos tiene lugar:
Cada vez que se realiza una actualización, siguen los siguientes eventos:
UIScreen
objetos enfocadovista
propiedad se cambia a la vista de que el enfoque se está moviendo a.didUpdateFocusInContext (_: withAnimationCoordinator :)
de cada objeto de entorno de foco involucrado en la actualización de foco. Estos son el mismo conjunto de objetos que el motor de enfoque verifica llamando a cada objeto shouldUpdateFocusInContext (_ :)
Método antes de actualizar el foco. Es en este punto que puede agregar animaciones personalizadas para ejecutar junto con las animaciones relacionadas con el enfoque que proporciona el sistema.Para actualizar manualmente el enfoque en la interfaz de usuario, puede invocar el setNeedsFocusUpdate ()
Método de cualquier objeto de entorno de enfoque. Esto restablece el enfoque y lo regresa al entorno preferredFocusView
.
El sistema también puede activar una actualización automática del enfoque en varias situaciones, incluso cuando se elimina una vista enfocada de la jerarquía de vistas, una vista de tabla o colección vuelve a cargar sus datos, o cuando se presenta o se despide un nuevo controlador de vista.
Si bien el motor de enfoque de tvOS es bastante complejo y tiene muchas partes móviles, las API de UIKit que se le proporcionan facilitan el uso de este sistema y lo hacen funcionar como usted quiere..
Para extender el motor de enfoque, vamos a implementar un comportamiento envolvente. Nuestra aplicación actual tiene una cuadrícula de seis botones como se muestra en la siguiente captura de pantalla.
Lo que vamos a hacer es permitir al usuario mover el enfoque hacia la derecha, desde los botones 3 y 6, y hacer que el enfoque vuelva a los botones 1 y 4, respectivamente. Como el motor de enfoque ignora las vistas invisibles, esto no se puede hacer insertando un invisible Vista
(incluyendo una vista con una anchura y altura de 0) y cambiando su preferredFocusedView
propiedad.
En su lugar, podemos lograr esto usando el UIFocusGuide
clase. Esta clase es una subclase de UILayoutGuide
y representa una región rectangular enfocable en la pantalla, mientras que es completamente invisible y no interactúa con la jerarquía de vistas. Encima de todo el UILayoutGuide
propiedades y métodos, la UIFocusGuide
La clase agrega las siguientes propiedades:
preferredFocusedView
: Esta propiedad funciona como lo describí anteriormente. Puede pensar en esto como la vista en la que desea que la guía de enfoque se redirija a.habilitado
: Esta propiedad le permite activar o desactivar la guía de enfoque.En tu proyecto, abre. ViewController.swift e implementar el viewDidAppear (_ :)
método de la ViewController
clase como se muestra a continuación:
anular func viewDidAppear (animated: Bool) super.viewDidAppear (animated) let rightButtonIds = [3, 6] para buttonId in rightButtonIds if let button = buttonWithTag (buttonId) (let focusGuide = UIFocusGuide () view.addLayoutirect) .................................... o....................................... (button.centerYAnchor) .active = true focusGuide.preferredFocusedView = buttonWithTag (buttonId-2) let leftButtonIds = [1, 4] para buttonId en leftButtonIds if let button = buttonWithTag (buttonId) let focusGuide = .addLayoutGuide (focusGuide) focusGuide.widthAnchor.constraintEqualToAnchor (button.widthAnchor) .active = true focusGuide.heightAnchor.constraintEqualToAnchor (button.heightAnchor) .active = true focusGuia.tr ailingAnchor.constraintEqualToAnchor (button.leadingAnchor, constante: -60.0) .active = true focusGuide.centerYAnchor.constraintEqualToAnchor (button.contrate con este botón).
En viewDidAppear (_ :)
, Creamos guías de enfoque a la derecha de los botones 3 y 6, ya la izquierda de los botones 1y 4. Dado que estas guías de enfoque representan una región enfocable en la interfaz de usuario, deben tener una altura y una anchura determinadas. Con este código, hacemos que las regiones tengan el mismo tamaño que los otros botones para que la lógica basada en el impulso del motor de enfoque se sienta coherente con los botones visibles.
Para ilustrar cómo funcionan las animaciones coordinadas, actualizamos el alfa
Propiedad de los botones cuando cambia el foco. En ViewController.swift, implementar el didUpdateFocusInContext (_: withAnimationCoordinator :)
método en el ViewController
clase:
override func didUpdateFocusInContext (context: UIFocusUpdateContext, withAnimationCoordinator coordinator: UIFocusAnimationCoordinator) super.didUpdateFocusInContext (context, withAnimationCoordinator) coordinador) UButton donde buttons.contains (FocusButton) coordinator.addCoordinatedAnimations (focusButton.alpha = 0.5, finalización: // Ejecutar animación completada)
los contexto
parámetro de didUpdateFocusInContext (_: withAnimationCoordinator :)
es un UIFocusUpdateContext
Objeto que tiene las siguientes propiedades:
previousFocusedView
: hace referencia a la vista desde la que se mueve el foconextFocusedView
: hace referencia a la vista a la que se está moviendo el foco.Enfoque
: una UIFocusHeading
valor de enumeración que representa la dirección en la que se está moviendo el focoCon la implementación de didUpdateFocusInContext (_: withAnimationCoordinator :)
, agregamos una animación coordinada para cambiar el valor alfa del botón enfocado previamente a 0.5 y el del botón enfocado actualmente a 1.0.
Ejecute la aplicación en el simulador y mueva el foco entre los botones en la interfaz de usuario. Puede ver que el botón enfocado actualmente tiene un alfa de 1.0 mientras que el botón enfocado anteriormente tiene un alfa de 0.5.
El primer cierre de la addCoordinatedAnimations (_: completar :)
método funciona de manera similar a un regular Vista
animación de cierre. La diferencia es que hereda su función de duración y tiempo del motor de enfoque..
Si desea ejecutar una animación con una duración personalizada, puede agregar cualquier Vista
Animación dentro de este cierre con la OverrideInheritedDuration
Opción de animación. El siguiente código es un ejemplo de cómo implementar una animación personalizada que se ejecuta en la mitad del tiempo de las animaciones de enfoque:
// Ejecutar animación personalizada programada let duration = UIView.inheritedAnimationDuration () UIView.animateWithDuration (duration / 2.0, delay: 0.0, options: .OverrideInheritedDuration, animations: // Animations, completar: (completed: Bool) en // Bloque de completacion)
Utilizando el UIFocusGuide
Clase y al utilizar animaciones personalizadas, puede extender el comportamiento estándar del motor de enfoque de tvOS para satisfacer sus necesidades.
Como mencioné anteriormente, cuando se decide si el enfoque debe moverse o no de una vista a otra, el motor de enfoque llama al shouldUpdateFocusInContext (_ :)
Método en cada entorno de enfoque involucrado. Si alguno de estos métodos llama devuelve falso
, el enfoque no se cambia.
En nuestra aplicación, vamos a anular este método en el ViewController
clase para que el foco no se pueda mover hacia abajo si el botón enfocado actualmente es 2 o 3. Para hacerlo, implemente shouldUpdateFocusInContext (_ :)
en el ViewController
clase como se muestra a continuación:
anular la función shouldUpdateFocusInContext (context: UIFocusUpdateContext) -> Bool let focusButton = context.previouslyFocusedView as? UIButton si enfocadoButón == buttonWithTag (2) || focusButton == buttonWithTag (3) if context.focusHeading == .Down return false return super.shouldUpdateFocusInContext (context)
En shouldUpdateFocusInContext (_ :)
, Primero verificamos si la vista enfocada previamente es el botón 2 o 3. Luego inspeccionamos el encabezado de enfoque. Si el encabezado es igual a Abajo
, regresamos falso
para que el enfoque actual no cambie.
Ejecuta tu aplicación una última vez. No puede mover el foco hacia abajo de los botones 2 y 3 a los botones 5 y 6.
Ahora debería estar cómodo controlando y trabajando con el motor de enfoque de tvOS. Ahora sabe cómo funciona el motor de enfoque y cómo puede manipularlo para adaptarse a las necesidades que tenga para sus propias aplicaciones de Apple TV.
Como siempre, asegúrese de dejar sus comentarios y sugerencias en los comentarios a continuación.