Tar kontroll över tvOS Focus Engine

Introduktion

På IOS brukar användarna normalt interagera med dina appar via enhetens pekskärm. På tvOS hanteras användarinteraktionen genom att flytta strömmen fokus mellan vyerna på skärmen.

Lyckligtvis hanterar tvOS-implementeringarna av UIKit API: er ändring av fokus mellan visningar automatiskt. Även om det här inbyggda systemet fungerar mycket bra, för specifika visningslayouter och / eller ändamål, kan det vara nödvändigt att ibland manuellt styra fokusmotor.

I denna handledning tar vi en djupgående titt på tvOS-fokusmotorn. Du lär dig hur det fungerar och hur man kontrollerar det men du vill.

förutsättningar

Denna handledning kräver att du kör Xcode 7.3 eller senare med den senaste TVOS 9.2 SDK. Om du vill följa med måste du också ladda ner startprojektet från GitHub.

1. Fokusmotoröversikt

Syftet med fokusmotorn för tvOS är att hjälpa utvecklare att koncentrera sig på sin egen apps unika innehåll snarare än att genomföra grundläggande navigationsbeteenden. Det betyder att även om många användare använder Siri Remote från Apple TV, stöder fokusmotorn automatiskt alla nuvarande och framtida Apple TV-inmatningsenheter.

Det innebär att du som utvecklare inte behöver oroa dig för hur en användare interagerar med din app. Ett annat viktigt mål med fokusmotorn är att skapa en konsekvent användarupplevelse mellan applikationer. På grund av detta finns inget API som tillåter en applikation att flytta fokus.

Fokusrörelse

När användaren interagerar med fjärrkontrollen på Apple TV genom att svepa på glaset. Touchytan i en viss riktning söker fokusmotorn efter en möjlig fokuseringsvy i den riktningen och, om den hittas, flyttar fokuset till den vyn. Om ingen fokuserbar vy hittas, återstår fokusen där den befinner sig.

Förutom att flytta fokusen i en viss riktning hanterar fokusmotorn också flera andra, mer avancerade beteenden, såsom:

  • Flytta fokusen förbi specifika vyer om användaren exempelvis sveper snabbt på Apple TVs fjärrkontroll
  • kör animationer med hastigheter baserat på hastigheten på fokusändringen
  • spelar navigeringsljud när fokus ändras
  • animera rullningsvisningar förskjutningar automatiskt när fokus behöver flyttas till en aktuell skärmvy

När du bestämmer var fokus ska flyttas i en app tar fokusmotorn en intern bild av din apps nuvarande gränssnitt och lyfter fram alla synliga element som kan fokuseras. Det innebär att alla dolda vyer, inklusive visningar med ett alfavärde på 0, inte kan fokuseras. Detta innebär också att för varje syn som är gömd av en annan vy, beaktas endast den synliga delen av fokusmotorn.

Om fokusmotorn finner en vy kan den flytta fokus till, det meddelar objekten som överensstämmer med UIFocusEnvironment protokoll som är inblandade i förändringen. UIKit klasserna som överensstämmer med UIFocusEnvironment protokollet är UIWindow, UIViewControllerUIView, och UIPresentationController. Fokusmotorn kallar shouldUpdateFocusInContext (_ :) Metod för alla fokusmiljöobjekt som innehåller antingen den aktuellt fokuserade vyn eller den vy som fokuset flyttar till. Om någon av dessa metodanrop returnerar falsk, fokusen ändras inte.

Initial Focus

De UIFocusEnvironment protokollet representerar ett objekt som är känt som a fokus miljö. Protokollet definierar a preferredFocusView egenskap som specificerar var fokus ska flyttas till om den aktuella miljön fokuseras själv.

Till exempel a UIViewController objektets standard preferredFocusView är dess rotvy. Som varje UIView objekt kan också ange sin egen föredragna fokusvy, a föredragen fokuskedja kan skapas. TVOS-fokusmotorn följer denna kedja tills ett visst objekt returnerar heller själv eller noll från dess preferredFocusView fast egendom. Genom att använda dessa egenskaper kan du omdirigera fokus över användargränssnittet och även ange vilken vy som ska fokuseras först när en bildkontroll visas på skärmen.

Det är viktigt att notera att om du inte ändrar någon av de preferredFocusView Egenskaper för dina synpunkter och visningskontroller, fokuserar fokusinställningen motorn synvinkeln närmast till det övre vänstra hörnet på skärmen.

Fokusuppdatering

En fokusuppdatering inträffar när en av tre händelser äger rum:

  • användaren orsakar en fokusrörelse
  • Appen begär uttryckligen en fokusuppdatering
  • systemet triggar och automatisk uppdatering

När en uppdatering äger rum följer följande händelser:

  • Nuvarande UIScreen objekt focusedView egendomen ändras till sikten att fokus flyttar till.
  • Fokusmotorn kallar didUpdateFocusInContext (_: withAnimationCoordinator :) av alla fokusmiljöobjekt som är inblandade i fokusuppdateringen. Dessa är samma uppsättning objekt som fokusmotorn kontrollerar genom att anropa varje objekt shouldUpdateFocusInContext (_ :) metod innan du uppdaterar fokus. Det är vid denna tidpunkt du kan lägga till anpassade animeringar för att köras i samband med de fokusrelaterade animationer som systemet ger.
  • Alla samordnade animationer, både system och anpassade animeringar, körs samtidigt.
  • Om vyn som fokusen flyttas till är för närvarande utanför skärmen och i en rullningsvy rullar systemet vyn på skärmen så att vyn blir synlig för användaren.

För att manuellt uppdatera fokuset i användargränssnittet kan du påkalla setNeedsFocusUpdate () metod för något fokusmiljöobjekt. Detta återställer fokuset och flyttar det tillbaka till miljön preferredFocusView.

Systemet kan också utlösa en automatisk fokusuppdatering i flera situationer, inklusive när en fokuserad vy tas bort från visningshierarkin, en tabell eller samlingsvy laddar upp sin data, eller när en ny bildkontroll visas eller avvisas.

Medan tvOS-fokusmotorn är ganska komplex och har många rörliga delar, ger UIKit API-erna till dig det mycket enkelt att använda detta system och gör det att fungera hur du vill att det ska.

2. Styra fokusmotorn

Fokusguider

För att utöka fokusmotorn ska vi genomföra ett omslutningsbeteende. Vår nuvarande app har ett rutnät med sex knappar som visas i nedanstående skärmdump.

Vad vi ska göra är att tillåta användaren att flytta fokusen åt höger, från knapparna 3 och 6, och fokusera omslaget till respektive knapparna 1 och 4. Eftersom fokusmotorn ignorerar osynliga vyer kan detta inte göras genom att infoga en osynlig UIView (inklusive en vy med en bredd och höjd av 0) och ändra dess preferredFocusedView fast egendom.

Istället kan vi uppnå detta med hjälp av UIFocusGuide klass. Denna klass är en underklass av UILayoutGuide och representerar en rektangulär fokusbar region på skärmen medan den är helt osynlig och inte interagerar med vyhierarkin. På toppen av alla UILayoutGuide egenskaper och metoder, UIFocusGuide klassen lägger till följande egenskaper:

  • preferredFocusedView: Den här egenskapen fungerar som jag beskrivit tidigare. Du kan tänka på detta som den uppfattning att du vill att fokusguiden ska omdirigera till.
  • aktiverad: Med den här egenskapen kan du aktivera eller inaktivera fokusguiden.

I ditt projekt, öppna ViewController.swift och implementera viewDidAppear (_ :) metod för ViewController klass som visas nedan:

åsidosätta func viewDidAppear (animerad: Bool) super.viewDidAppear (animerad) låt rightButtonIds = [3, 6] för buttonId i rightButtonIds om låt knappen = buttonWithTag (buttonId) let 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) släpp leftButtonIds = [1, 4] för knappenId i vänsterButtonIds om låt knappen = knappenWithTag (buttonId) let 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 skapar fokusguider till höger om knapparna 3 och 6 och till vänster om knapparna 1och 4. Eftersom dessa fokusguider representerar en fokuserbar region i användargränssnittet måste de ha en uppsättning höjd och bredd. Med denna kod gör vi regionerna lika stora som de andra knapparna så att fokusmotorens momentumbaserade logik överensstämmer med de synliga knapparna.

Koordinerade animationer

För att illustrera hur samordnade animationer fungerar uppdaterar vi alfa Egenskapen för knapparna när fokusen ändras. I ViewController.swift, implementera didUpdateFocusInContext (_: withAnimationCoordinator :) metod i ViewController klass:

åsidosätta func didUpdateFocusInContext (context: UIFocusUpdateContext, medAnimationCoordinator koordinator: UIFocusAnimationCoordinator) super.didUpdateFocusInContext (context, withAnimationCoordinator: coordinator) om låt focusButton = context.previouslyFocusedView som? UIButton där buttons.contains (focusedButton) coordinator.addCoordinatedAnimations (focusedButton.alpha = 0.5, slutförd: // Kör avslutad animering)

De sammanhang parameter för didUpdateFocusInContext (_: withAnimationCoordinator :) är en UIFocusUpdateContext objekt som har följande egenskaper:

  • previouslyFocusedView: refererar till den vy fokusen flyttar från
  • nextFocusedView: refererar till vyn fokuset flyttar till
  • focusHeading: a UIFocusHeading uppräkningsvärde som representerar den riktning fokusen rör sig i

Med genomförandet av didUpdateFocusInContext (_: withAnimationCoordinator :), Vi lägger till en samordnad animering för att ändra alfavärdet för den tidigare fokuserade knappen till 0,5 och den för den aktuella fokuserade knappen till 1.0.

Kör appen i simulatorn och flytta fokus mellan knapparna i användargränssnittet. Du kan se att den för närvarande inriktade knappen har en alfa på 1,0 medan den tidigare fokuserade knappen har en alfa på 0,5.

Den första stängningen av addCoordinatedAnimations (_: fullbordan :) Metoden fungerar på samma sätt som en vanlig UIView animering stängning. Skillnaden är att den ärverver sin varaktighet och tidsfunktion från fokusmotorn.

Om du vill köra en animering med en anpassad varaktighet kan du lägga till något UIView animering inom denna stängning med OverrideInheritedDuration animeringsalternativ. Följande kod är ett exempel på hur man implementerar en anpassad animering som körs på halva tiden för fokusanimationerna:

// Löpande anpassad tidsinriktad animering låt varaktighet = UIView.inheritedAnimationDuration () UIView.animateWithDuration (duration / 2.0, delay: 0.0, options: .OverrideInheritedDuration, animeringar: // Animationer, slutförande: (färdigställt: Bool) i // Färdigställningsblock)

Genom att använda UIFocusGuide klass och genom att använda anpassade animeringar, kan du förlänga standardbeteendet hos tvOS-fokusmotorn så att det passar dina behov.

Begränsa fokusmotorn

Som jag nämnde tidigare, när man bestämmer huruvida fokus ska flyttas från en vy till en annan, ringer fokusmotorn på shouldUpdateFocusInContext (_ :) metod för varje fokusmiljö som är inblandad. Om någon av dessa metodanrop returnerar falsk, fokusen ändras inte.

I vår app kommer vi att åsidosätta denna metod i ViewController klass så att fokus inte kan flyttas om den aktuella fokuserade knappen är 2 eller 3. För att göra det, implementera shouldUpdateFocusInContext (_ :) i ViewController klass som visas nedan:

åsidosätta func shouldUpdateFocusInContext (context: UIFocusUpdateContext) -> Bool let focusedButton = context.previouslyFocusedView som? UIButton om focusButton == buttonWithTag (2) || focusButton == buttonWithTag (3) om context.focusHeading == .Down return false returnera super.shouldUpdateFocusInContext (context)

shouldUpdateFocusInContext (_ :), Vi kontrollerar först om den tidigare fokuserade vyn är knapp 2 eller 3. Vi inspekterar sedan fokusrubriken. Om rubriken är lika med Ner, vi återvänder falsk så att nuvarande fokus inte ändras.

Kör din app en gång förra gången. Du kan inte flytta fokusen ner från knapparna 2 och 3 till knapparna 5 och 6.

Slutsats

Du borde nu vara bekväm att kontrollera och arbeta med TVOS-fokusmotorn. Du vet nu hur fokusmotorn fungerar och hur du kan manipulera den så att den passar vad du behöver för dina egna Apple TV-appar.

Som alltid, var noga med att lämna dina kommentarer och feedback i kommentarerna nedan.