Prendre le contrôle de tvOS Focus Engine

introduction

Sur iOS, les utilisateurs interagissent normalement avec vos applications via l'écran tactile de l'appareil. Sur tvOS, toutefois, l’interaction de l’utilisateur est gérée en déplaçant le concentrer entre les vues à l'écran.

Heureusement, les implémentations tvOS des API UIKit gèrent automatiquement le changement de focus entre les vues. Bien que ce système intégré fonctionne très bien, pour des dispositions de vue et / ou des objectifs spécifiques, il peut parfois être nécessaire de contrôler manuellement le moteur de mise au point..

Dans ce tutoriel, nous examinons en détail le moteur de mise au point tvOS. Vous apprendrez comment cela fonctionne et comment le contrôler comme vous le souhaitez..

Conditions préalables

Ce tutoriel nécessite que vous exécutiez Xcode 7.3 ou une version ultérieure avec le dernier SDK tvOS 9.2. Si vous souhaitez suivre, vous devez également télécharger le projet de démarrage à partir de GitHub..

1. Aperçu du moteur de focus

Le moteur de focus de tvOS vise à aider les développeurs à se concentrer sur le contenu unique de leur propre application plutôt que de réimplémenter les comportements de navigation de base. Cela signifie que, même si de nombreux utilisateurs utiliseront la télécommande Siri d'Apple TV, le moteur de focus prend automatiquement en charge tous les périphériques d'entrée Apple TV actuels et futurs..

Cela signifie qu'en tant que développeur, vous n'avez pas à vous soucier de la manière dont un utilisateur interagit avec votre application. Un autre objectif important du moteur de focus est de créer une expérience utilisateur cohérente entre les applications. De ce fait, aucune API ne permet à une application de déplacer le focus..

Mouvement de focus

Lorsque l'utilisateur interagit avec la télécommande de l'Apple TV en glissant la surface tactile du verre dans une direction particulière, le moteur de mise au point recherche une vue pouvant être mise au point dans cette direction et, le cas échéant, déplace le focus sur cette vue. Si aucune vue focalisable n'est trouvée, la focalisation reste telle qu'elle est actuellement..

En plus de déplacer le focus dans une direction particulière, le moteur de focus gère également plusieurs autres comportements plus avancés, tels que:

  • déplacer le focus au-delà de vues particulières si, par exemple, l'utilisateur fait glisser rapidement la surface tactile de la télécommande Apple TV
  • exécuter des animations à des vitesses basées sur la vélocité du changement de focus
  • jouer des sons de navigation lorsque la mise au point change
  • animer automatiquement les décalages de la vue de défilement lorsque le focus doit être déplacé vers une vue actuellement hors écran

Lorsqu'il détermine l'endroit où le focus doit être déplacé dans une application, le moteur de focus prend une photo interne de l'interface actuelle de votre application et met en surbrillance tous les éléments visibles pouvant être mis au point. Cela signifie que toute vue masquée, y compris les vues avec une valeur alpha de 0, ne peut pas être mise au point. Cela signifie également que, pour toute vue masquée par une autre vue, seule la partie visible est prise en compte par le moteur de mise au point..

Si le moteur de focus trouve une vue vers laquelle il peut déplacer le focus, il avertit les objets conformes à la UIFocusEnvironment protocole qui sont impliqués dans le changement. Les classes UIKit qui se conforment à la UIFocusEnvironment protocole sont UIWindow, UIViewControllerUIView, et UIPresentationController. Le moteur de mise au point appelle le shouldUpdateFocusInContext (_ :) méthode de tous les objets d’environnement de focus qui contiennent la vue actuellement active ou celle vers laquelle le focus se déplace. Si l'une de ces méthodes appelle retourne faux, le focus n'est pas changé.

Mise au point initiale

le UIFocusEnvironment protocole représente un objet qui est connu sous le nom environnement de mise au point. Le protocole définit un PreferredFocusView propriété qui spécifie où le focus doit être déplacé si l'environnement actuel se concentre lui-même.

Par exemple, un UIViewController défaut de l'objet PreferredFocusView est sa vue racine. Comme chacun UIView objet peut également spécifier sa propre vue focalisée préférée, une chaîne de discussion préférée peut être créé. Le moteur de mise au point tvOS suit cette chaîne jusqu'au retour d'un objet particulier soi ou néant de son PreferredFocusView propriété. En utilisant ces propriétés, vous pouvez rediriger le focus sur l’ensemble de l’interface utilisateur et spécifier également la vue à focaliser en premier lorsqu'un contrôleur de vue apparaît à l’écran..

Il est important de noter que, si vous ne changez aucun des PreferredFocusView propriétés de vos vues et contrôleurs de vue, le moteur de focus par défaut focalise la vue la plus proche du coin supérieur gauche de l'écran.

Mise à jour de la mise au point

Une mise à jour de la mise au point se produit lorsqu'un des trois événements suivants se produit:

  • l'utilisateur provoque un mouvement de focus
  • l'application demande explicitement une mise à jour de focus
  • les déclencheurs du système et la mise à jour automatique

Chaque fois qu'une mise à jour a lieu, les événements suivants suivent:

  • Le courant UIScreen objets focalisé la propriété est modifiée en vue que le focus se déplace vers.
  • Le moteur de mise au point appelle le didUpdateFocusInContext (_: withAnimationCoordinator :) de chaque objet d’environnement de focus impliqué dans la mise à jour de focus. Il s’agit du même ensemble d’objets que le moteur de recherche vérifie en appelant chaque objet. shouldUpdateFocusInContext (_ :) méthode avant de mettre à jour le focus. C’est à ce stade que vous pouvez ajouter des animations personnalisées à exécuter en conjonction avec les animations relatives au focus fournies par le système..
  • Toutes les animations coordonnées, les animations système et personnalisées, sont exécutées simultanément.
  • Si la vue vers laquelle se déplace le focus est actuellement hors écran et dans une vue à défilement, le système fait défiler la vue à l'écran de manière à ce que la vue soit visible par l'utilisateur..

Pour mettre à jour manuellement le focus dans l'interface utilisateur, vous pouvez appeler le setNeedsFocusUpdate () méthode de tout objet d’environnement de focus. Ceci réinitialise le focus et le ramène à l'environnement PreferredFocusView.

Le système peut également déclencher une mise à jour automatique de la focalisation dans plusieurs situations, notamment lorsqu'une vue focalisée est supprimée de la hiérarchie des vues, qu'une vue de table ou de collection recharge ses données ou lorsqu'un nouveau contrôleur de vue est présenté ou supprimé..

Bien que le moteur de mise au point tvOS soit assez complexe et comporte de nombreuses pièces mobiles, les API UIKit fournies facilitent grandement l’utilisation de ce système et lui permettent de fonctionner comme vous le souhaitez..

2. Contrôle du moteur de focus

Guides de mise au point

Pour étendre le moteur de focus, nous allons implémenter un comportement de bouclage. Notre application actuelle a une grille de six boutons, comme indiqué dans la capture d'écran ci-dessous.

Ce que nous allons faire, c'est permettre à l'utilisateur de déplacer le focus vers la droite, des boutons 3 et 6, et de le ramener à celui des boutons 1 et 4, respectivement. Comme le moteur de focus ignore les vues invisibles, vous ne pouvez pas y parvenir en insérant un élément invisible. UIView (y compris une vue avec une largeur et une hauteur de 0) et en changeant sa PreferredFocusedView propriété.

Au lieu de cela, nous pouvons accomplir cela en utilisant le UIFocusGuide classe. Cette classe est une sous-classe de UILayoutGuide et représente une zone de mise au point rectangulaire sur l'écran tout en étant complètement invisible et n'interagissant pas avec la hiérarchie des vues. En plus de tout UILayoutGuide propriétés et méthodes, le UIFocusGuide La classe ajoute les propriétés suivantes:

  • PreferredFocusedView: Cette propriété fonctionne comme je l'ai décrit précédemment. Vous pouvez considérer cela comme la vue vers laquelle vous souhaitez que le guide de focus se dirige.
  • activée: Cette propriété vous permet d'activer ou de désactiver le guide de focus.

Dans votre projet, ouvrez ViewController.swift et mettre en œuvre le viewDidAppear (_ :) méthode du ViewController classe comme indiqué ci-dessous:

remplacer func viewDidAppear (animation: Bool) super.viewDidAppear (animation) let rightButtonIds = [3, 6] pour buttonId dans rightButtonIds if let button = buttonWithTag (buttonId) let focusGuide = UIFocusGuide () view.addLayout (focus) .widthAnchor.constraintEqualToAnchor (button.widthAnchor) .active = true focusGuide.leadingAnchor.constraintEqualToAnchor (button.heightAnchor). (button.centerYAnchor) .active = true focusGuide.preferredFocusedView = buttonWithTag (buttonId-2) let leftButtonIds = [1, 4] pour buttonId dans la vue leftButtonIds si let button = buttonWithTag (buttonId)  .addLayoutGuide (focusGuide) focusGuide.widthAnchor.constraintEqualToAnchor (button.widthAnchor) .active = true focusGuide.heightAnchor.constraintEqualToAnchor (button.heightAnchor) .active = true focusGuide.tr ailingAnchor.constraintEqualToAnchor (button.leadingAnchor, constante: -60,0) .active = true focusGuide.centerYAnchor.constraintEqualToAnchor (button.centerYAnchor) .active = true focusGuide.preferredFocusedView = buttonWithTag (buttonId + 2)

Dans viewDidAppear (_ :), nous créons des guides de mise au point à droite des boutons 3 et 6 et à gauche des boutons 1et 4. Étant donné que ces guides de mise au point représentent une zone de mise au point dans l'interface utilisateur, ils doivent avoir une hauteur et une largeur définies. Avec ce code, nous faisons en sorte que les régions aient la même taille que les autres boutons, de sorte que la logique du moteur de focus basée sur la quantité de mouvement semble cohérente avec les boutons visibles..

Animations coordonnées

Pour illustrer le fonctionnement des animations coordonnées, nous mettons à jour le alpha propriété des boutons lorsque le focus change. Dans ViewController.swift, mettre en œuvre le didUpdateFocusInContext (_: withAnimationCoordinator :) méthode dans le ViewController classe:

override func didUpdateFocusInContext (contexte: UIFocusUpdateContext, avec coordinateur AnimationCoordinator: UIFocusAnimationCoordinator) super.didUpdateFocusInContext (contexte, avec AnimationCoordinator: coordinator) si laissé focusButton = context.previouslyVoir comme? UIButton où buttons.contains (FocusButton) coordinator.addCoordinatedAnimations (FocusButton.alpha = 0.5, complétion: // Exécuter l'animation terminée)

le le contexte paramètre de didUpdateFocusInContext (_: withAnimationCoordinator :) est un UIFocusUpdateContext objet qui a les propriétés suivantes:

  • précédemmentFocusedView: fait référence à la vue à partir de laquelle le focus se déplace
  • nextFocusedView: fait référence à la vue vers laquelle se déplace le focus
  • focusHeading: une UIFocusHeading valeur d'énumération représentant la direction dans laquelle le foyer se déplace

Avec la mise en place de didUpdateFocusInContext (_: withAnimationCoordinator :), nous ajoutons une animation coordonnée pour changer la valeur alpha du bouton précédemment activé à 0,5 et celle du bouton actuellement activé à 1,0.

Exécutez l'application dans le simulateur et déplacez le focus entre les boutons de l'interface utilisateur. Vous pouvez voir que le bouton actuellement activé a un alpha de 1,0 alors que le bouton précédemment activé a un alpha de 0,5.

La première fermeture de la addCoordinatedAnimations (_: complétion :) méthode fonctionne de manière similaire à un régulier UIView fermeture de l'animation. La différence est qu’il hérite de sa durée et de sa fonction de synchronisation du moteur de focus.

Si vous souhaitez exécuter une animation avec une durée personnalisée, vous pouvez ajouter UIView animation dans cette fermeture avec le OverrideInheritedDuration option d'animation. Le code suivant illustre comment implémenter une animation personnalisée qui s'exécute deux fois plus vite que les animations de focus:

// Exécution d'une animation personnalisée personnalisée: durée = UIView.inheritedAnimationDuration () UIView.animateWithDuration (duration / 2.0, délai: 0,0, options: .OverrideInheritedDuration, animations: // Animations, complétion: (complété: Bool) dans // Bloc d'achèvement)

En utilisant le UIFocusGuide En utilisant des animations personnalisées, vous pouvez étendre le comportement standard du moteur de mise au point tvOS à vos besoins..

Limiter le moteur de focus

Comme je l'ai mentionné précédemment, lorsqu'il décide si le focus doit ou non être déplacé d'une vue à une autre, le moteur de la mise au point appelle le shouldUpdateFocusInContext (_ :) méthode sur chaque environnement de mise au point impliqué. Si l'une de ces méthodes appelle retourne faux, le focus n'est pas changé.

Dans notre application, nous allons remplacer cette méthode dans le ViewController classe de sorte que le focus ne puisse pas être déplacé vers le bas si le bouton actuellement sélectionné est 2 ou 3. Pour ce faire, implémentez shouldUpdateFocusInContext (_ :) dans le ViewController classe comme indiqué ci-dessous:

Remplacer func devraitUpdateFocusInContext (contexte: UIFocusUpdateContext) -> Bool laissez focusButton = context.previouslyFocusedView as? UIButton si FocusButton == buttonWithTag (2) || FocusButton == buttonWithTag (3) si context.focusHeading == .Down return false retour super.shouldUpdateFocusInContext (context)

Dans shouldUpdateFocusInContext (_ :), nous vérifions d'abord si la vue précédemment focalisée est le bouton 2 ou 3. Nous inspectons ensuite l'en-tête de focus. Si le titre est égal à Vers le bas, nous retournons faux de sorte que l'accent actuel ne change pas.

Exécutez votre application une dernière fois. Vous ne pouvez pas déplacer le focus des boutons 2 et 3 vers les boutons 5 et 6.

Conclusion

Vous devriez maintenant être à l'aise avec le contrôle et l'utilisation du moteur de focus de tvOS. Vous savez maintenant comment fonctionne le moteur de focus et comment vous pouvez le manipuler pour répondre à tous vos besoins pour vos propres applications Apple TV..

Comme toujours, assurez-vous de laisser vos commentaires dans les commentaires ci-dessous.