Tar kontroll over tvOS Focus Engine

Introduksjon

På IOS, brukerne normalt interaksjon med appene dine via enhetens berøringsskjerm. På tvOS håndteres imidlertid brukerinteraksjonen ved å flytte strømmen fokus mellom visninger på skjermen.

Heldigvis håndterer tvOS-implementeringene av UIKit API-ene endring av fokus mellom visninger automatisk. Selv om dette innebygde systemet fungerer veldig bra, for bestemte visningslogger og / eller formål, kan det være nødvendig å noen ganger manuelt kontrollere fokusmotor.

I denne opplæringen tar vi en grundig titt på tvOS fokusmotor. Du lærer hvordan det fungerer og hvordan du kontrollerer det, men du vil.

Forutsetninger

Denne opplæringen krever at du kjører Xcode 7.3 eller høyere med den nyeste TVOS 9.2 SDK. Hvis du vil følge med, må du også laste ned startprosjektet fra GitHub.

1. Fokus Motor Oversikt

Formålet med fokusmotoren til tvOS er å hjelpe utviklere med å konsentrere seg om sin egen apps unike innhold, i stedet for å reimplementere grunnleggende navigeringsoppføringer. Dette betyr at, mens mange brukere vil bruke Apple TVs Siri Remote, støtter fokusmotor automatisk alle nåværende og fremtidige Apple TV-inngangsenheter.

Dette betyr at du som utvikler ikke trenger å bekymre deg for hvordan en bruker samhandler med appen din. Et annet viktig mål med fokusmotor er å skape en konsistent brukeropplevelse mellom applikasjoner. På grunn av dette er det ingen API som lar et program flytte fokuset.

Fokusbevegelse

Når brukeren samhandler med fjernkontrollen til Apple TV ved å sveipe på glasset. Berøringsflaten i en bestemt retning, ser fokusmotorene etter en mulig fokuserbar visning i den retningen og, hvis funnet, flytter fokuset til den visningen. Hvis ingen fokuserbar visning er funnet, forblir fokuset der det er for øyeblikket.

I tillegg til å flytte fokuset i en bestemt retning, håndterer fokusmotoren også flere andre, mer avanserte atferd, som for eksempel:

  • Flytte fokuset forbi bestemte visninger hvis brukeren f.eks. svinger fast på Touch-overflaten på Apple TV-fjernkontrollen
  • kjører animasjoner med hastigheter basert på hastigheten på fokusendringen
  • spiller navigeringslyder når fokuset endres
  • animasjonsrulle vise forskyvninger automatisk når fokuset må flyttes til en for øyeblikket ikke-skjermvisning

Når du bestemmer hvor fokus skal flyttes til i en app, tar fokusmotoren et internt bilde av appens nåværende grensesnitt og fremhever alle de synlige elementene som kan fokuseres. Dette betyr at eventuelle skjulte visninger, inkludert visninger med en alfa-verdi på 0, ikke kan fokuseres. Dette betyr også at for enhver visning som er skjult av en annen visning, anses kun den synlige delen av fokusmotoren.

Hvis fokusmotoren finner en visning, kan den flytte fokuset til, det underretter objektene som samsvarer med UIFocusEnvironment protokoll som er involvert i endringen. UIKit klassene som samsvarer med UIFocusEnvironment protokollen er UIWindow, UIViewControllerUIView, og UIPresentationController. Fokusmotoren kaller shouldUpdateFocusInContext (_ :) metode for alle fokusmiljøobjekter som inneholder enten den nåværende fokuserte visningen eller visningen fokuset beveger seg til. Hvis noen av disse metodene ringer tilbake falsk, fokuset er ikke endret.

Initial Focus

De UIFocusEnvironment protokollen representerer et objekt som er kjent som en fokus miljø. Protokollen definerer a preferredFocusView Egenskap som spesifiserer hvor fokus skal flyttes til hvis det nåværende miljøet fokuserer seg selv.

For eksempel a UIViewController objektets standard preferredFocusView er roten sin. Som hver UIView objekt kan også spesifisere sin egen foretrukne fokusvisning, a foretrukket fokuskjede kan opprettes. TvOS fokusmotor følger denne kjeden til en bestemt gjenstand returnerer heller selv- eller nil fra sin preferredFocusView eiendom. Ved å bruke disse egenskapene kan du omdirigere fokus gjennom brukergrensesnittet og også spesifisere hvilken visning som skal fokuseres først når en visningsstyring vises på skjermen.

Det er viktig å merke seg at hvis du ikke endrer noen av preferredFocusView egenskapene til visningene dine og visning av kontroller, fokuserer fokuset som standard motoren visningen nærmest øverst til venstre på skjermen.

Fokusoppdatering

En fokusoppdatering oppstår når en av tre hendelser finner sted:

  • brukeren forårsaker en fokusbevegelse
  • Appen krever eksplisitt en fokusoppdatering
  • systemet utløser og automatisk oppdatering

Når en oppdatering finner sted, følger følgende hendelser:

  • Den nåværende UIScreen objektets focusedView Eiendommen er endret til utsikten at fokuset beveger seg til.
  • Fokusmotoren kaller didUpdateFocusInContext (_: withAnimationCoordinator :) av alle fokusmiljøobjekter involvert i fokusoppdateringen. Dette er det samme settet med objekter som fokusmotoren kontrollerer ved å ringe til hvert objekt shouldUpdateFocusInContext (_ :) metode før oppdatering av fokus. Det er på dette tidspunktet at du kan legge til egendefinerte animasjoner for å kjøre sammen med de fokusrelaterte animasjonene som systemet gir.
  • Alle de koordinerte animasjonene, både system og tilpassede animasjoner, kjøres samtidig.
  • Hvis visningen fokuset flyttes til, er for øyeblikket ikke på skjermen, og i en rullevisning ruller systemet visningen på skjermen slik at visningen blir synlig for brukeren.

For å manuelt oppdatere fokuset i brukergrensesnittet, kan du påkalle setNeedsFocusUpdate () metode for hvilken som helst fokusmiljøobjekt. Dette nullstiller fokuset og flytter det tilbake til miljøet preferredFocusView.

Systemet kan også utløse en automatisk fokusoppdatering i flere situasjoner, inkludert når en fokusert visning fjernes fra visningshierarkiet, oppdateres en tabell eller samlingsvisning dataene sine, eller når en ny visningscontroller blir presentert eller avvist.

Mens tvOS-fokusmotor er ganske kompleks og har mange bevegelige deler, gir UIKit API-ene til deg det svært enkelt å bruke dette systemet og gjør det til å fungere slik du vil.

2. Kontrollere fokusmotoren

Fokusveiledninger

For å utvide fokusmotor, skal vi implementere en omviklingsadferd. Vår nåværende app har et rutenett på seks knapper som vist på bildet nedenfor.

Det vi skal gjøre er å la brukeren flytte fokuset til høyre, fra knappene 3 og 6, og la fokuset vikle tilbake til henholdsvis knappene 1 og 4. Som fokusmotor ignorerer noen usynlige visninger, kan dette ikke gjøres ved å sette inn en usynlig UIView (inkludert en visning med bredde og høyde på 0) og endre dens preferredFocusedView eiendom.

I stedet kan vi oppnå dette ved hjelp av UIFocusGuide klasse. Denne klassen er en underklasse av UILayoutGuide og representerer en rektangulær fokusbar region på skjermen mens den er helt usynlig og ikke interagerer med visningshierarkiet. På toppen av alle UILayoutGuide egenskaper og metoder, UIFocusGuide Klassen legger til følgende egenskaper:

  • preferredFocusedView: Denne egenskapen fungerer som beskrevet tidligere. Du kan tenke på dette som visningen om at du vil at fokusveiledningen skal viderekobles til.
  • aktivert: Denne egenskapen lar deg aktivere eller deaktivere fokusveiledningen.

I prosjektet ditt, åpne ViewController.swift og implementere viewDidAppear (_ :) metode av ViewController klassen som vist nedenfor:

overstyr func viewDidAppear (animert: Bool) super.viewDidAppear (animert) la rightButtonIds = [3, 6] for buttonId i rightButtonIds hvis la knappen = knappWithTag (buttonId) la focusGuide = UIFocusGuide () view.addLayoutGuide (focusGuide) focusGuide .widthAnchor.constraintEqualToAnchor (button.widthAnchor) .active = true focusGuide.heightAnchor.constraintEqualToAnchor (button.heightAnchor) .active = true focusGuide.leadingAnchor.constraintEqualToAnchor (button.trailingAnchor, constant: 60.0) .active = true focusGuide.centerYAnchor.constraintEqualToAnchor (button.centerYAnchor) .active = true focusGuide.preferredFocusedView = buttonWithTag (buttonId-2) la leftButtonIds = [1, 4] for buttonId i leftButtonIds hvis la knapp = buttonWithTag (buttonId) la focusGuide = UIFocusGuide .addLayoutGuide (focusGuide) focusGuide.widthAnchor.constraintEqualToAnchor (button.widthAnchor) .active = true focusGuide.heightAnchor.constraintEqualToAnchor (button.heightAnchor) .active = true focusGuide.tr ailingAnchor.constraintEqualToAnchor (button.leadingAnchor, constant: -60.0) .active = true focusGuide.centerYAnchor.constraintEqualToAnchor (button.centerYAnchor) .active = true focusGuide.preferredFocusedView = buttonWithTag (buttonId + 2)

I viewDidAppear (_ :), Vi lager fokusguider til høyre for knappene 3 og 6, og til venstre for knappene 1og 4. Da disse fokusguider representerer en fokuserbar region i brukergrensesnittet, må de ha en sett høyde og bredde. Med denne koden, gjør vi regionene i samme størrelse som de andre knappene, slik at den momentumbaserte logikken til fokusmotor føles i samsvar med de synlige knappene.

Koordinert animasjon

For å illustrere hvordan samordnede animasjoner fungerer, oppdaterer vi alfa Egenskapen til knappene når fokuset endres. I ViewController.swift, implementere didUpdateFocusInContext (_: withAnimationCoordinator :) metode i ViewController klasse:

overstyr func didUpdateFocusInContext (kontekst: UIFocusUpdateContext, medAnimationCoordinator koordinator: UIFocusAnimationCoordinator) super.didUpdateFocusInContext (kontekst, medAnimationCoordinator: koordinator) hvis la focusedButton = context.previouslyFocusedView som? UIButton hvor buttons.contains (focusedButton) coordinator.addCoordinatedAnimations (focusedButton.alpha = 0.5, ferdigstillelse: // Kjør fullført animasjon)

De kontekst parameter av didUpdateFocusInContext (_: withAnimationCoordinator :) er en UIFocusUpdateContext objekt som har følgende egenskaper:

  • previouslyFocusedView: refererer til visningen fokuset beveger seg fra
  • nextFocusedView: refererer til visningen fokuset beveger seg på
  • focusHeading: a UIFocusHeading tallverdi som representerer retningen fokuset beveger seg inn i

Med implementeringen av didUpdateFocusInContext (_: withAnimationCoordinator :), vi legger til en koordinert animasjon for å endre alfaverdien til den tidligere fokuserte knappen til 0,5 og den aktuelle fokuserte knappen til 1.0.

Kjør appen i simulatoren og flytt fokus mellom knappene i brukergrensesnittet. Du kan se at den nåværende fokuserte knappen har en alfa på 1,0 mens den tidligere fokuserte knappen har en alfa på 0,5.

Den første nedleggelsen av addCoordinatedAnimations (_: fullføring :) Metoden fungerer på samme måte som en vanlig UIView animasjonstenging. Forskjellen er at den arver sin varighet og timingfunksjon fra fokusmotor.

Hvis du vil kjøre en animasjon med en tilpasset varighet, kan du legge til noen UIView animasjon innen denne lukkingen med OverrideInheritedDuration animasjonsalternativ. Følgende kode er et eksempel på hvordan du implementerer en egendefinert animasjon som går i løpet av halvparten av fokusanimasjonene:

// Kjør tilpasset tidsbegrenset animasjon la duration = UIView.inheritedAnimationDuration () UIView.animateWithDuration (varighet / 2,0, forsinkelse: 0.0, alternativer: .OverrideInheritedDuration, animasjoner: // Animasjoner, fullføring: (ferdig: Bool) i // Fullføringsblokk)

Ved å bruke UIFocusGuide klassen og ved å bruke tilpassede animasjoner, kan du utvide standardoppførelsen til tvOS fokusmotor som passer dine behov.

Begrensning av fokusmotoren

Som jeg nevnte tidligere, når jeg bestemmer hvorvidt fokuset skal flyttes fra et synspunkt til et annet, ringer fokusmotoren på shouldUpdateFocusInContext (_ :) metode for hvert fokus miljø involvert. Hvis noen av disse metodene ringer tilbake falsk, fokuset er ikke endret.

I vår app skal vi overstyre denne metoden i ViewController klassen slik at fokuset ikke kan flyttes ned hvis den nåværende fokuserte knappen er 2 eller 3. For å gjøre det, implementer shouldUpdateFocusInContext (_ :) i ViewController klassen som vist nedenfor:

overstyr func shouldUpdateFocusInContext (kontekst: UIFocusUpdateContext) -> Bool la focusedButton = context.previouslyFocusedView som? UIButton hvis fokusertButton == buttonWithTag (2) || focusButton == buttonWithTag (3) hvis context.focusHeading == .Down return false returnere super.shouldUpdateFocusInContext (kontekst)

shouldUpdateFocusInContext (_ :), Vi kontrollerer først om den tidligere fokuserte visningen er knapp 2 eller 3. Vi kontrollerer deretter fokusoverskriften. Hvis overskriften er lik Ned, vi returnerer falsk slik at dagens fokus ikke endres.

Kjør appen din forrige gang. Du kan ikke flytte fokuset ned fra knappene 2 og 3 til knappene 5 og 6.

Konklusjon

Du bør nå være komfortabel å kontrollere og jobbe med fokusmotoren til tvOS. Du vet nå hvordan fokusmotoren fungerer, og hvordan du kan manipulere den for å passe uansett behov du har for dine egne Apple TV-apper.

Som alltid, vær sikker på å legge igjen dine kommentarer og tilbakemeldinger i kommentarene nedenfor.