Op iOS communiceren gebruikers normaal gesproken met uw apps via het aanraakscherm van het apparaat. Bij tvOS wordt de gebruikersinteractie afgehandeld door de stroom te verplaatsen focus tussen weergaven op het scherm.
Gelukkig zorgen de tvOS-implementaties van de UIKit API's ervoor dat de focus automatisch wordt gewijzigd tussen weergaven. Hoewel dit ingebouwde systeem erg goed werkt, kan het voor specifieke indelingslay-outs en / of doeleinden soms nodig zijn om de focusengine handmatig te besturen.
In deze zelfstudie bekijken we de tvOS-focusengine grondig. Je leert hoe het werkt en hoe je het kunt besturen hoe je wilt.
Voor deze zelfstudie moet je Xcode 7.3 of hoger gebruiken met de nieuwste tvOS 9.2 SDK. Als je mee wilt, moet je ook het startersproject van GitHub downloaden.
Het doel van de focusengine van tvOS is om ontwikkelaars te helpen zich te concentreren op de unieke inhoud van hun eigen app in plaats van het basisnavigatiegedrag opnieuw uit te voeren. Dit betekent dat, hoewel veel gebruikers de Siri Remote van Apple TV zullen gebruiken, de focus-engine automatisch alle huidige en toekomstige Apple TV-invoerapparaten ondersteunt.
Dit betekent dat u als ontwikkelaar geen zorgen hoeft te maken over hoe een gebruiker met uw app omgaat. Een ander belangrijk doel van de focusengine is het creëren van een consistente gebruikerservaring tussen applicaties. Daarom is er geen API waarmee een toepassing de focus kan verplaatsen.
Wanneer de gebruiker interactie heeft met de afstandsbediening van de Apple TV door op het aanraakoppervlak van het glas in een bepaalde richting te vegen, zoekt de focusmotor naar een mogelijk focusseerbaar beeld in die richting en verplaatst de focus, indien gevonden, naar die weergave. Als er geen focusbare weergave wordt gevonden, blijft de focus waar deze zich op dat moment bevindt.
Naast het verplaatsen van de focus in een bepaalde richting, behandelt de focusengine ook verschillende andere, geavanceerdere gedragingen, zoals:
Bij het bepalen waar de focus naartoe moet gaan in een app, maakt de focusengine een interne afbeelding van de huidige interface van uw app en markeert alle zichtbare elementen die kunnen worden scherpgesteld. Dit betekent dat verborgen weergaven, inclusief weergaven met een alpha-waarde van 0, niet kunnen worden scherpgesteld. Dit betekent ook dat voor elke weergave die is verborgen door een andere weergave, alleen het zichtbare gedeelte door de focusengine wordt beschouwd.
Als de focusengine een weergave vindt die de focus kan verplaatsen, geeft deze de objecten die aan de UIFocusEnvironment
protocol dat betrokken is bij de verandering. De UIKit-klassen die voldoen aan de UIFocusEnvironment
protocol zijn UIWindow
, UIViewController
, UIView
, en UIPresentationController
. De focusmotor roept de shouldUpdateFocusInContext (_ :)
methode van alle focusomgeving-objecten die ofwel de huidige gefocuste weergave bevatten of de weergave waarnaar de focus verplaatst. Als een van deze methoden oproepen retourneert vals
, de focus is niet veranderd.
De UIFocusEnvironment
protocol staat voor een object dat bekend staat als a focus omgeving. Het protocol definieert een preferredFocusView
eigenschap die aangeeft waar de focus naartoe zou moeten gaan als de huidige omgeving zelf wordt gefocust.
Bijvoorbeeld a UIViewController
de standaard van het object preferredFocusView
is de rootweergave. Zoals elke UIView
object kan ook zijn eigen gewenste focusweergave specificeren, a gewenste focusketen kan worden gemaakt. De focusmotor van tvOS volgt deze keten tot een bepaald object ook terugkeert zelf
of nul
van zijn preferredFocusView
eigendom. Door deze eigenschappen te gebruiken, kunt u de focus omleiden door de gebruikersinterface en ook opgeven welke weergave als eerste moet worden scherpgesteld wanneer een weergaveregelaar op het scherm wordt weergegeven.
Het is belangrijk om op te merken dat, als u geen van de preferredFocusView
eigenschappen van uw weergaven en view controllers, de focus by default engine richt de weergave die zich het dichtst bij de linkerbovenhoek van het scherm bevindt.
Een focusupdate vindt plaats wanneer een van de drie gebeurtenissen plaatsvindt:
Wanneer een update plaatsvindt, volgen de volgende gebeurtenissen:
UIScreen
voorwerpen focusedView
eigenschap is gewijzigd in de weergave waarnaar de focus wordt verplaatst.didUpdateFocusInContext (_: withAnimationCoordinator :)
van elk focusomgevingobject dat betrokken is bij de focusupdate. Dit zijn dezelfde set objecten die de focusengine controleert door het object van elk object te bellen shouldUpdateFocusInContext (_ :)
methode voordat de focus wordt bijgewerkt. Op dit punt kunt u aangepaste animaties toevoegen om te gebruiken in combinatie met de focusgerelateerde animaties die het systeem biedt.Om de focus in de gebruikersinterface handmatig bij te werken, kunt u de setNeedsFocusUpdate ()
methode van elk focusomgevingobject. Dit stelt de focus opnieuw in en verplaatst deze terug naar de omgeving preferredFocusView
.
Het systeem kan ook een automatische focusupdate activeren in verschillende situaties, inclusief wanneer een gefocuste weergave wordt verwijderd uit de weergavehiërarchie, een tabel of collectieweergave de gegevens herlaadt of wanneer een nieuwe weergavecontroller wordt gepresenteerd of ontslagen.
Hoewel de focusmotor van tvOS vrij complex is en veel bewegende delen bevat, maken de UIKit-API's die aan u worden verstrekt het zeer gemakkelijk om dit systeem te gebruiken en het te laten werken zoals u het wilt.
Om de focusengine uit te breiden, gaan we een wrap-around-gedrag implementeren. Onze huidige app heeft een raster van zes knoppen, zoals weergegeven in de onderstaande schermafbeelding.
Wat we gaan doen is de gebruiker toestaan om de focus naar rechts te verplaatsen, van de knoppen 3 en 6, en de focus terug te wikkelen naar knoppen 1 en 4 respectievelijk. Omdat de focusmachine geen onzichtbare weergaven negeert, kan dit niet worden gedaan door een onzichtbare in te voegen UIView
(inclusief een weergave met een breedte en hoogte van 0) en de bijbehorende te wijzigen preferredFocusedView
eigendom.
In plaats daarvan kunnen we dit bereiken met behulp van de UIFocusGuide
klasse. Deze klasse is een subklasse van UILayoutGuide
en vertegenwoordigt een rechthoekig focusseerbaar gebied op het scherm terwijl het volledig onzichtbaar is en geen interactie heeft met de beeldhiërarchie. Op de top van alle UILayoutGuide
eigenschappen en methoden, de UIFocusGuide
klasse voegt de volgende eigenschappen toe:
preferredFocusedView
: Deze eigenschap werkt zoals ik eerder heb beschreven. U kunt dit zien als de weergave waarnaar u in de focusgids wilt doorsturen.ingeschakeld
: Met deze eigenschap kunt u de focusgids in- of uitschakelen.In uw project, open ViewController.swift en implementeer de viewDidAppear (_ :)
methode van de ViewController
klasse zoals hieronder getoond:
override func viewDidAppear (geanimeerd: Bool) super.viewDidAppear (geanimeerd) laat rightButtonIds = [3, 6] voor buttonId in rightButtonIds if let button = 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) laat leftButtonIds = [1, 4] voor buttonId in leftButtonIds if let button = buttonWithTag (buttonId) let focusGuide = UIFocusGuide () view .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)
In viewDidAppear (_ :)
, we maken focusgidsen rechts van de knoppen 3 en 6 en links van de knoppen 1en 4. Aangezien deze focusgeleiders een focusseerbaar gebied in de gebruikersinterface vertegenwoordigen, moeten ze een ingestelde hoogte en breedte hebben. Met deze code maken we de gebieden even groot als de andere knoppen, zodat de op het momentum gebaseerde logica van de focusengine consistent aanvoelt met de zichtbare knoppen.
Om te illustreren hoe gecoördineerde animaties werken, werken we de alpha
eigenschap van de knoppen wanneer de focus verandert. In ViewController.swift, implementeer de didUpdateFocusInContext (_: withAnimationCoordinator :)
methode in de ViewController
klasse:
negeer func didUpdateFocusInContext (context: UIFocusUpdateContext, metAnimationCoordinator coordinator: UIFocusAnimationCoordinator) super.didUpdateFocusInContext (context, withAnimationCoordinator: coordinator) if let focusedButton = context.previouslyFocusedView als? UIButton waar buttons.contains (focusedButton) coordinator.addCoordinatedAnimations (focusedButton.alpha = 0.5, completion: // voltooide animatie uitvoeren)
De context
parameter van didUpdateFocusInContext (_: withAnimationCoordinator :)
is een UIFocusUpdateContext
object met de volgende eigenschappen:
previouslyFocusedView
: verwijst naar de weergave van waaruit de focus verschuiftnextFocusedView
: verwijst naar de weergave waar de focus naartoe gaatfocusHeading
: een UIFocusHeading
enumeratiewaarde die de richting aangeeft waarin de focus zich verplaatstMet de implementatie van didUpdateFocusInContext (_: withAnimationCoordinator :)
, we voegen een gecoördineerde animatie toe om de alpha-waarde van de eerder gefocuste knop te wijzigen in 0,5 en die van de momenteel gefocuste knop in 1.0.
Start de app in de simulator en verplaats de focus tussen de knoppen in de gebruikersinterface. U kunt zien dat de knop die momenteel is scherpgesteld een alpha van 1.0 heeft, terwijl de knop die eerder is scherpgesteld een alpha van 0.5 heeft.
De eerste sluiting van de addCoordinatedAnimations (_: voltooiing :)
methode werkt op dezelfde manier als een regulier UIView
animatie sluiting. Het verschil is dat het de duur en timingfunctie van de focusmachine overneemt.
Als u een animatie met een aangepaste duur wilt uitvoeren, kunt u deze toevoegen UIView
animatie binnen deze sluiting met de OverrideInheritedDuration
animatie optie. De volgende code is een voorbeeld van het implementeren van een aangepaste animatie die in de helft van de tijd van de focusanimaties wordt uitgevoerd:
// Aangepaste getimede animaties laten doorlopen = UIView.inheritedAnimationDuration () UIView.animateWithDuration (duur / 2.0, vertraging: 0.0, opties: .OverrideInheritedDuration, animaties: // Animations, completion: (completed: Bool) in // Voltooiingsblok)
Door de UIFocusGuide
klasse en door gebruik te maken van aangepaste animaties, kunt u het standaardgedrag van de tvOS-focusengine uitbreiden om aan uw behoeften te voldoen.
Zoals ik eerder al zei, vraagt de focusmachine om het bepalen of de focus al dan niet van de ene naar de andere moet worden verplaatst shouldUpdateFocusInContext (_ :)
methode op elke betrokken focusomgeving. Als een van deze methoden oproepen retourneert vals
, de focus is niet veranderd.
In onze app gaan we deze methode negeren in de ViewController
klasse zodat de focus niet naar beneden kan worden verplaatst als de op dat moment scherpgestelde knop 2 of 3 is. Implementeer dit shouldUpdateFocusInContext (_ :)
in de ViewController
klasse zoals hieronder getoond:
override func shouldUpdateFocusInContext (context: UIFocusUpdateContext) -> Bool let focusedButton = context.previouslyFocusedView as? UIButton if focusedButton == buttonWithTag (2) || focusedButton == buttonWithTag (3) if context.focusHeading == .Down return false retourneer super.shouldUpdateFocusInContext (context)
In shouldUpdateFocusInContext (_ :)
, we controleren eerst of de eerder gefocuste weergave knop 2 of 3 is. We inspecteren vervolgens de focuskop. Als de kop gelijk is aan naar beneden
, we komen terug vals
zodat de huidige focus niet verandert.
Voer uw app nog één keer uit. U kunt de focus niet verplaatsen van de knoppen 2 en 3 naar de knoppen 5 en 6.
Je zou nu comfortabel moeten zijn met het besturen en werken met de focus-engine van tvOS. Je weet nu hoe de focusengine werkt en hoe je hem kunt manipuleren om te voldoen aan wat je maar nodig hebt voor je eigen Apple TV-apps.
Laat zoals altijd uw opmerkingen en feedback achter in de opmerkingen hieronder.