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.
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.
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.
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:
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
, UIViewController
, UIView
, 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.
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.
En fokusuppdatering inträffar när en av tre händelser äger rum:
När en uppdatering äger rum följer följande händelser:
UIScreen
objekt focusedView
egendomen ändras till sikten att fokus flyttar till.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.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.
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.
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ånnextFocusedView
: refererar till vyn fokuset flyttar tillfocusHeading
: a UIFocusHeading
uppräkningsvärde som representerar den riktning fokusen rör sig iMed 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.
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)
I 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.
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.