Controlul motorului Focus tvOS

Introducere

În iOS, utilizatorii interacționează în mod normal cu aplicațiile dvs. prin intermediul ecranului tactil al dispozitivului. Pe tvOS, cu toate acestea, interacțiunea cu utilizatorul este manipulată prin mutarea curentului concentra între vizualizările de pe ecran.

Din fericire, implementările tvOS ale API-urilor UIKit se ocupă de schimbarea focalizării între vizualizări în mod automat. În timp ce acest sistem încorporat funcționează foarte bine, pentru aspecte și / sau scopuri specifice de vizualizare, poate fi necesar să controlați uneori manual motorul de focalizare.

În acest tutorial, analizăm în profunzime motorul de focalizare tvOS. Învățați cum funcționează și cum să o controlați oricum doriți.

Cerințe preliminare

Acest tutorial necesită să executați Xcode 7.3 sau o versiune ulterioară cu cea mai recentă SDOS 9.2 SDK. Dacă doriți să continuați, trebuie să descărcați proiectul de pornire de la GitHub.

1. Prezentarea generală a motorului de focalizare

Scopul motorului de focalizare al tvOS este de a ajuta dezvoltatorii să se concentreze asupra conținutului unic al aplicației proprii, în loc să reimplementeze comportamentele de navigație de bază. Acest lucru înseamnă că, în timp ce mulți utilizatori vor folosi Siri Remote Apple TV, motorul de focalizare acceptă automat toate dispozitivele de intrare Apple TV actuale și viitoare.

Aceasta înseamnă că, în calitate de dezvoltator, nu trebuie să vă faceți griji cu privire la modul în care un utilizator interacționează cu aplicația dvs. Un alt obiectiv important al motorului de focalizare este de a crea o experiență constantă între utilizatori. Din acest motiv, nu există niciun API care să permită unei aplicații să deplaseze focalizarea.

Mișcarea de focalizare

Atunci când utilizatorul interacționează cu telecomanda Apple TV prin deplasarea pe geam atingeți suprafața într-o anumită direcție, motorul de focalizare caută o vizibilă posibilă în această direcție și, dacă se găsește, mișcă focalizarea spre acea vizualizare. Dacă nu se găsește o vizualizare focalizată, focalizarea rămâne acolo unde este în prezent.

În plus față de mutarea focalizării într-o anumită direcție, motorul de focalizare se ocupă și de alte comportamente mai avansate, cum ar fi:

  • mișcarea focalizării asupra unor vizionări particulare, dacă, de exemplu, utilizatorul trece rapid pe suprafața Touch a telecomenzii Apple TV
  • rularea animațiilor la viteze bazate pe viteza schimbării de focalizare
  • redarea sunetelor de navigare când se schimbă focalizarea
  • animarea vizualizărilor de derulare se face automat atunci când focalizarea trebuie să se mute la o vizualizare în prezent pe ecran

Când determinați unde trebuie să treacă concentrarea într-o aplicație, motorul de focalizare are o imagine internă a interfeței curente a aplicației dvs. și evidențiază toate elementele vizibile care pot fi focalizate. Aceasta înseamnă că orice vizualizări ascunse, inclusiv vederi cu o valoare alfa de 0, nu pot fi focalizate. Aceasta înseamnă că, pentru orice vizualizare ascunsă de o altă imagine, numai partea vizibilă este considerată de motorul de focalizare.

Dacă motorul de focalizare găsește o vizualizare pe care o poate deplasa, acesta notifică obiectele conforme cu UIFocusEnvironment care sunt implicate în schimbarea. Clasele UIKit care sunt conforme cu UIFocusEnvironment protocol sunt UIWindow, UIViewControllerUIView, și UIPresentationController. Motorul de focalizare sună shouldUpdateFocusInContext (_ :) metoda tuturor obiectelor de mediu de focalizare care conțin fie viziunea curentă concentrată, fie viziunea pe care se îndreaptă focalizarea. Dacă oricare dintre aceste apeluri de metodă revine fals, focalizarea nu se schimbă.

Focus inițial

UIFocusEnvironment protocol reprezintă un obiect cunoscut sub numele de a mediu de focalizare. Protocolul definește a preferredFocusView proprietate care specifică unde trebuie să se deplaseze focalizarea în cazul în care mediul actual se focalizează în sine.

De exemplu, a UIViewController obiect implicit preferredFocusView este punctul său de vedere. Ca fiecare UIView Obiectul poate specifica și propria sa viziune de focalizare preferată, a lanțul de focalizare preferat pot fi create. Motorul de focalizare tvOS urmează acest lanț până când un anumit obiect nu se întoarce de sine sau zero de la ei preferredFocusView proprietate. Prin utilizarea acestor proprietăți, puteți redirecționa focalizarea pe întreaga interfață a utilizatorului și, de asemenea, să specificați ce vizualizare trebuie să fie focalizată mai întâi când un controler de vizualizare apare pe ecran.

Este important să rețineți că, dacă nu schimbați nimic preferredFocusView proprietățile vizualizărilor și controlorilor de vizualizare, focalizarea motorului implicit focalizează vizualizarea cea mai apropiată de colțul din stânga sus al ecranului.

Focus Update

O actualizare a focalizării are loc atunci când are loc unul din cele trei evenimente:

  • utilizatorul provoacă o mișcare de focalizare
  • aplicația solicită în mod explicit o actualizare a focalizării
  • sistemul declanșează și actualizează automat

Ori de câte ori are loc o actualizare, urmează următoarele evenimente:

  • Curent UIScreen obiecte focusedView proprietatea este schimbată în viziunea la care se îndreaptă atenția.
  • Motorul de focalizare sună didUpdateFocusInContext (_: withAnimationCoordinator :) din fiecare obiect de mediu de focalizare implicat în actualizarea focalizării. Acestea sunt aceleași seturi de obiecte pe care motorul de focalizare le verifică prin apelarea fiecărui obiect shouldUpdateFocusInContext (_ :) înainte de a actualiza focalizarea. În acest moment, puteți adăuga animații personalizate pentru a rula împreună cu animațiile legate de focalizare pe care le oferă sistemul.
  • Toate animațiile coordonate, atât animații de sistem cât și personalizate, sunt difuzate simultan.
  • Dacă vizualizarea în care se îndreaptă focalizarea este în prezent off-screen și într-o vizualizare de derulare, sistemul derulează vizualizarea pe ecran astfel încât vizualizarea să devină vizibilă pentru utilizator.

Pentru a actualiza manual focalizarea în interfața cu utilizatorul, puteți invoca funcția setNeedsFocusUpdate () metoda oricărui obiect de mediu de focalizare. Aceasta restabilește focalizarea și o mută înapoi în mediul înconjurător preferredFocusView.

De asemenea, sistemul poate declanșa o actualizare automată a focalizării în mai multe situații, inclusiv în cazul în care o vizualizare focalizată este eliminată din ierarhia de vizualizare, o vizualizare a tabelei sau a colecției își reîncarcă datele sau când este prezentat sau respins un nou controler de vizualizare.

În timp ce motorul de focalizare tvOS este destul de complex și are o mulțime de piese în mișcare, API-urile UIKit oferite vă fac foarte ușor să utilizați acest sistem și să îl faceți să funcționeze așa cum doriți.

2. Controlul motorului de focalizare

Focus Guides

Pentru a extinde motorul de focalizare, vom implementa un comportament înfășurat. Aplicația noastră curentă are o grilă de șase butoane, după cum se arată în ecranul de mai jos.

Ceea ce vom face este să permiteți utilizatorului să deplaseze focalizarea spre dreapta, de la butoanele 3 și 6 și să înfășoare focalizarea înapoi spre butoanele 1 și respectiv 4. Pe măsură ce motorul de focalizare ignoră orice vizualizare invizibilă, acest lucru nu se poate face prin introducerea unui invizibil UIView (inclusiv o vedere cu o lățime și înălțime de 0) și schimbarea lui preferredFocusedView proprietate.

În schimb, putem realiza acest lucru folosind UIFocusGuide clasă. Această clasă este o subclasă de UILayoutGuide și reprezintă o regiune rectangulară focalizabilă pe ecran în timp ce este complet invizibilă și nu interacționează cu ierarhia vizuală. Pe partea de sus a tuturor UILayoutGuide proprietăți și metode, UIFocusGuide clasa adaugă următoarele proprietăți:

  • preferredFocusedView: Această proprietate funcționează așa cum am descris mai devreme. Vă puteți gândi la acest lucru ca la punctul în care doriți să se redirecționeze ghidul de focalizare.
  • activat: Această proprietate vă permite să activați sau să dezactivați ghidul de focalizare.

În proiectul dvs., deschis ViewController.swift și să pună în aplicare viewDidAppear (_ :) metodă a ViewController clasa după cum se arată mai jos:

override functie viewDidAppear (animat: Bool) super.viewDidAppear (animat) lasati rightButtonIds = [3, 6] pentru butonulIndicator in dreaptaButtonIds daca butonul da = butonulWithTag (butonulId) focusGuide = UIFocusGuide (focusGuide) focusGuide .widthAnchor.constraintEqualToAnchor (buton.widthAnchor) .active = true focusGuide.heightAnchor.constraintEqualToAnchor (buton.heightAnchor) .active = true focusGuide.leadingAnchor.constraintEqualToAnchor (buton.trailingAnchor, constant: 60.0) .active = true focusGuide.centerYAnchor.constraintEqualToAnchor (button.centerYAnchor) .activ = adevărat focusGuide.preferredFocusedView = butonulWithTag (buttonId-2) leftButtonIds = [1, 4] pentru buttonId în leftButtonIds if let = buttonWithTag (buttonId) let focusGuide = UIFocusGuide .addLayoutGuide (focusGuide) focusGuide.widthAnchor.constraintEqualToAnchor (buton.widthAnchor) .active = true focusGuide.heightAnchor.constraintEqualToAnchor (buton.heightAnchor) .active = true focusGuide.tr ailingAnchor.constraintEqualToAnchor (buton.leadingAnchor, constant: -60.0) .activ = adevărat focusGuide.centerYAnchor.constraintEqualToAnchor (buton.centerYAnchor) .activ = adevărat focusGuide.preferredFocusedView = butonWithTag (buttonId + 2)

În viewDidAppear (_ :), vom crea ghiduri de focalizare în partea dreaptă a butoanelor 3 și 6 și în partea stângă a butoanelor 1și 4. Deoarece aceste ghiduri de focus reprezintă o regiune focalizabilă în interfața cu utilizatorul, ele trebuie să aibă o înălțime și o lățime stabilite. Cu acest cod, facem ca regiunile să aibă aceeași dimensiune ca celelalte butoane, astfel încât logica bazată pe impulsuri a motorului de focalizare să se simtă conformă cu butoanele vizibile.

Animații coordonate

Pentru a ilustra modul în care funcționează animațiile coordonate, actualizăm alfa proprietățile butoanelor atunci când se schimbă focalizarea. În ViewController.swift, pune în aplicare didUpdateFocusInContext (_: withAnimationCoordinator :) metodă în ViewController clasă:

override funcția didUpdateFocusInContext (context: UIFocusUpdateContext, cuAnimationCoordinator coordinator: UIFocusAnimationCoordinator) super.didUpdateFocusInContext (context, cuAnimationCoordinator: coordinator) dacă permiteți focusButton = context.previouslyFocusedView as? UIButton unde buttons.contains (focusedButton) coordinator.addCoordinatedAnimations (focusedButton.alpha = 0.5, finalizarea: // Run animation completed)

context parametru de didUpdateFocusInContext (_: withAnimationCoordinator :) este a UIFocusUpdateContext obiect care are următoarele proprietăți:

  • previouslyFocusedView: se referă la viziunea la care se mișcă focalizarea
  • nextFocusedView: se referă la viziunea la care se îndreaptă atenția
  • focusHeading: A UIFocusHeading valoarea de numărare reprezentând direcția în care se mișcă focalizarea

Odată cu punerea în aplicare a didUpdateFocusInContext (_: withAnimationCoordinator :), adăugăm o animație coordonată pentru a schimba valoarea alfa a butonului focalizat anterior la valoarea 0.5 și cea a butonului focalizat în prezent la 1.0.

Rulați aplicația în simulator și mutați focalizarea între butoanele din interfața cu utilizatorul. Puteți vedea că butonul focalizat în prezent are un alfa de 1,0 în timp ce butonul focalizat anterior are un alfa de 0,5.

Prima închidere a addCoordinatedAnimations (_: finalizare :) metoda funcționează similar cu cea obișnuită UIView închiderea animației. Diferența este că îi moștenează durata și funcția de temporizare de la motorul de focalizare.

Dacă doriți să rulați o animație cu o durată personalizată, puteți adăuga orice UIView animație în cadrul acestei închideri cu OverrideInheritedDuration opțiune de animație. Următorul cod este un exemplu de implementare a unei animații personalizate care rulează în jumătate din timpul animațiilor de focalizare:

// Running custom animation timed let = UIView.inheritedAnimationDuration () UIView.animateWithDuration (durata / 2.0, întârziere: 0.0, opțiuni: .OverrideInheritedDuration, animații: // Animations, completare: (completat: Bool) în // Bloc de completare)

Prin utilizarea funcției UIFocusGuide clasa și prin utilizarea de animații personalizate, puteți extinde comportamentul standard al motorului de focalizare tvOS pentru a se potrivi nevoilor dvs..

Limitarea motorului de focalizare

Așa cum am menționat mai devreme, atunci când se decide dacă focalizarea ar trebui să fie mutată dintr-o vedere în alta, motorul de focalizare îl sună shouldUpdateFocusInContext (_ :) pe fiecare mediu de interes implicat. Dacă oricare dintre aceste apeluri de metodă revine fals, focalizarea nu se schimbă.

În aplicația noastră, vom trece peste această metodă în ViewController astfel încât focalizarea să nu poată fi mutată în jos dacă butonul focalizat în prezent este 2 sau 3. Pentru a face acest lucru, implementați shouldUpdateFocusInContext (_ :) în ViewController clasa după cum se arată mai jos:

override func shouldUpdateFocusInContext (context: UIFocusUpdateContext) -> Bool lasa focusButton = context.previouslyFocusedView as? UIButton dacă focusButton == butonulWithTag (2) || focusButton == butonulWithTag (3) if context.focusHeading == .Down return false retur super.shouldUpdateFocusInContext (context)

În shouldUpdateFocusInContext (_ :), mai întâi verificăm dacă vizualizarea focalizată anterior este butonul 2 sau 3. Atunci verificăm poziția focalizării. Dacă poziția este egală cu Jos, ne intoarcem fals astfel încât focalizarea actuală să nu se schimbe.

Rulați aplicația ultima oară. Nu puteți mișca focalizarea în jos de la butoanele 2 și 3 în butoanele 5 și 6.

Concluzie

Ar trebui să controlați și să lucrați confortabil cu motorul de focalizare al tvOS. Acum știți cum funcționează motorul de focalizare și cum îl puteți manipula pentru a se potrivi oricăror nevoi pe care le aveți pentru propriile aplicații Apple TV.

Ca întotdeauna, asigurați-vă că părăsiți comentariile și comentariile dvs. în comentariile de mai jos.

Cod