Înțelegerea delegatului de scenă iOS 13

Publicat de donnywals la 28 octombrie 201928 octombrie 2019

Când creați un nou proiect în Xcode 11, s-ar putea să observați ceva ce nu ați mai văzut până acum. În loc să creați doar un fișier AppDelegate.swift, un ViewController.swift, un storyboard și alte câteva fișiere, Xcode creează acum un nou fișier pentru dumneavoastră; fișierul SceneDelegate.swift. Dacă nu ați mai văzut acest fișier până acum, ar putea fi destul de confuz să înțelegeți ce este și cum ar trebui să utilizați acest nou delegat de scenă în aplicația dumneavoastră.

Până la sfârșitul articolului de blog din această săptămână veți ști:

  • Pentru ce este utilizat delegatul de scenă.
  • Cum puteți implementa eficient delegatul de scenă.
  • De ce delegatul de scenă este o parte importantă a iOS 13.

Să trecem direct la subiect, da?

Examinarea noului șablon de proiect Xcode

Când creați un nou proiect Xcode, aveți opțiunea de a alege dacă doriți să utilizați SwiftUI sau Storyboards. Indiferent de alegerea pe care o faceți aici, Xcode va genera un nou tip de șablon de proiect pe baza căruia să construiți. Vom examina mai îndeaproape fișierele SceneDelegate.swift și AppDelegate.swift în secțiunea următoare, ceea ce este important pentru moment este să înțelegeți că Xcode a creat aceste fișiere pentru dumneavoastră.

În plus față de aceste două fișiere delegate, Xcode face ceva puțin mai subtil. Aruncați o privire atentă la fișierul Info.plist. Ar trebui să vedeți o nouă cheie numită Application Scene Manifest cu un conținut similar cu imaginea următoare:

Captură de ecran a manifestului scenei din fișierul Info.plist

Captură de ecran a manifestului scenei din fișierul Info.plist

Acest manifest al scenei specifică un nume și o clasă de delegat pentru scena dumneavoastră. Rețineți că aceste proprietăți aparțin unui array (Application Session Role), ceea ce sugerează că puteți avea mai multe configurații în Info.plist dumneavoastră. O cheie mult mai importantă, pe care probabil ați observat-o deja în captura de ecran de mai sus, este Enable Multiple Windows. Această proprietate este setată în mod implicit la NO. Setarea acestei proprietăți la YES va permite utilizatorilor să deschidă mai multe ferestre ale aplicației dvs. pe iPadOS (sau chiar pe macOS). Posibilitatea de a rula mai multe ferestre ale unei aplicații iOS una lângă alta este o diferență uriașă față de mediul cu o singură fereastră cu care am lucrat până acum, iar capacitatea de a avea mai multe ferestre este întregul motiv pentru care ciclul de viață al aplicației noastre este acum menținut în două locuri, și nu într-unul singur.

Să aruncăm o privire mai atentă la AppDelegate și SceneDelegate pentru a înțelege mai bine modul în care acești doi delegați lucrează împreună pentru a permite suportul pentru ferestre multiple.

Înțelegerea rolurilor AppDelegate și SceneDelegate

Dacă ați construit aplicații înainte de iOS 13, probabil că știți că AppDelegate este singurul loc care face cam tot ceea ce este legat de lansarea aplicației, prim-plan, fundal și altele. În iOS 13, Apple a mutat o parte din responsabilitățile AppDelegate la SceneDelegate. Să aruncăm o scurtă privire asupra fiecăruia dintre aceste două fișiere.

Responsabilități ale luiAppDelegate

În iOS 13, AppDelegate este încă principalul punct de intrare pentru o aplicație. Apple apelează metodele AppDelegate pentru mai multe evenimente din ciclul de viață de la nivelul aplicației. În șablonul implicit al Apple veți găsi trei metode pe care Apple consideră că este important să le folosiți:

  • func application(_:didFinishLaunchingWithOptions:) -> Bool
  • func application(_:configurationForConnecting:options:) -> UISceneConfiguration
  • func application(_:didDiscardSceneSessions:)

Aceste metode au în ele unele comentarii care descriu de fapt ceea ce fac cu suficiente detalii pentru a înțelege ce fac. Dar haideți să le trecem în revistă rapid oricum.

Când aplicația dvs. tocmai a fost lansată, func application(_:didFinishLaunchingWithOptions:) -> Bool este apelată. Această metodă este utilizată pentru a efectua configurarea aplicației. În iOS 12 sau anterior, este posibil să fi folosit această metodă pentru a crea și configura un obiect UIWindow și să fi atribuit o instanță UIViewController ferestrei pentru a o face să apară.

Dacă aplicația dvs. utilizează scene, AppDelegate nu mai este responsabilă pentru a face acest lucru. Deoarece aplicația dvs. poate avea acum mai multe ferestre, sau UISceneSessions active, nu are prea mult sens să gestionați un singur obiect fereastră în AppDelegate.

Ceasul func application(_:configurationForConnecting:options:) -> UISceneConfiguration este apelat ori de câte ori se așteaptă ca aplicația dvs. să furnizeze o nouă scenă, sau o fereastră pentru ca iOS să o afișeze. Rețineți că această metodă nu este apelată atunci când aplicația dvs. se lansează inițial, ci este apelată doar pentru a obține și a crea scene noi. Vom examina mai în profunzime crearea și gestionarea mai multor scene într-o postare ulterioară pe blog.

Ultima metodă din șablonul AppDelegate este func application(_:didDiscardSceneSessions:). Această metodă este apelată ori de câte ori un utilizator renunță la o scenă, de exemplu prin îndepărtarea acesteia în fereastra de multitasking sau dacă faceți acest lucru în mod programatic. Dacă aplicația dvs. nu rulează atunci când utilizatorul face acest lucru, această metodă va fi apelată pentru fiecare scenă eliminată la scurt timp după ce este apelată func application(_:didFinishLaunchingWithOptions:) -> Bool.

În plus față de aceste metode implicite, AppDelegate dvs. mai poate fi folosit pentru a deschide URL-uri, pentru a prinde avertismente de memorie, pentru a detecta când aplicația dvs. se va încheia, dacă ceasul dispozitivului s-a schimbat semnificativ, pentru a detecta când un utilizator s-a înregistrat pentru notificări de la distanță și multe altele.

Sfat:
Este important să rețineți că, dacă utilizați în prezent AppDelegate pentru a gestiona aspectul barei de stare a aplicației dumneavoastră, este posibil să trebuiască să faceți unele modificări în iOS 13. Mai multe metode legate de bara de stare au fost depreciate în iOS 13.

Acum că avem o imagine mai bună a ceea ce sunt noile responsabilități ale AppDelegate dumneavoastră, haideți să aruncăm o privire la noul SceneDelegate.

Responsabilitățile lui SceneDelegate

Când considerați că AppDelegate este obiectul responsabil pentru ciclul de viață al aplicației dumneavoastră, SceneDelegate este responsabil pentru ceea ce este afișat pe ecran; scenele sau ferestrele. Înainte de a continua, haideți să stabilim un vocabular legat de scene, deoarece nu fiecare termen înseamnă ceea ce credeți că înseamnă.

Când aveți de-a face cu scenele, ceea ce pare a fi o fereastră pentru utilizator se numește de fapt o UIScene care este gestionată de un UISceneSession. Deci, atunci când ne referim la ferestre, ne referim de fapt la obiecte UISceneSession. Voi încerca să rămân la această terminologie cât mai mult posibil pe parcursul acestui articol de blog.

Acum că suntem pe aceeași pagină, să ne uităm la fișierul SceneDelegate.swift pe care Xcode l-a creat atunci când a creat proiectul nostru.

Există mai multe metode în fișierul SceneDelegate.swift în mod implicit:

  • scene(_:willConnectTo:options:)
  • sceneDidDisconnect(_:)
  • sceneDidBecomeActive(_:)
  • sceneWillResignActive(_:)
  • sceneWillEnterForeground(_:)
  • sceneDidEnterBackground(_:)

Aceste metode ar trebui să vă pară foarte familiare dacă sunteți familiarizați cu AppDelegate care a existat înainte de iOS 13. Să ne uităm mai întâi la scene(_:willConnectTo:options:), această metodă vă pare probabil cel mai puțin familiară și este prima metodă apelată în ciclul de viață al unei UISceneSession.

Implementarea implicită a scene(_:willConnectTo:options:) creează vizualizarea inițială a conținutului (ContentView dacă folosiți SwiftUI), creează o nouă UIWindow, stabilește rootViewController a ferestrei și face din această fereastră fereastră fereastra cheie. Vă puteți gândi la această fereastră ca fiind fereastra pe care o vede utilizatorul dumneavoastră. Din păcate, acesta nu este cazul. Ferestrele există încă dinainte de iOS 13 și reprezintă viewportul în care funcționează aplicația dvs. Astfel, UISceneSession controlează fereastra vizibilă pe care o vede utilizatorul, UIWindow pe care o creați este vizualizarea container pentru aplicația dvs.

În plus față de configurarea vizualizărilor inițiale, puteți utiliza scene(_:willConnectTo:options:) pentru a restabili interfața de utilizare a scenei în cazul în care scena dvs. s-a deconectat în trecut. De exemplu, pentru că a fost trimisă în fundal. De asemenea, puteți citi obiectul connectionOptions pentru a vedea dacă scena dvs. a fost creată din cauza unei cereri HandOff sau poate pentru a deschide un URL. Vă voi arăta cum să faceți acest lucru mai târziu în acest articol de blog.

După ce scena dvs. s-a conectat, următoarea metodă din ciclul de viață al scenei dvs. este sceneWillEnterForeground(_:). Această metodă este apelată atunci când scena dvs. va intra în scenă. Acest lucru ar putea fi atunci când aplicația dvs. trece din fundal în prim-plan sau dacă tocmai devine activă pentru prima dată. În continuare, este apelată sceneDidBecomeActive(_:). Acesta este punctul în care scena dvs. este configurată, vizibilă și gata de a fi utilizată.

Când aplicația dvs. trece în fundal, sunt apelate sceneWillResignActive(_:) și sceneDidEnterBackground(_:). Nu voi intra în aceste metode acum, deoarece scopul lor variază pentru fiecare aplicație, iar comentariile din șablonul Xcode fac o treabă destul de bună în a explica când sunt apelate aceste metode. De fapt, sunt sigur că vă puteți da seama singuri de momentul în care sunt apelate aceste metode.

O metodă mai interesantă este sceneDidDisconnect(_:). Ori de câte ori scena dvs. este trimisă în fundal, iOS ar putea decide să se deconecteze și să curețe scena dvs. pentru a elibera resurse. Acest lucru nu înseamnă că aplicația dvs. a fost ucisă sau că nu mai rulează, ci pur și simplu înseamnă că scena transmisă acestei metode nu mai este activă și se va deconecta de la sesiunea sa.

Rețineți că nici sesiunea în sine nu este neapărat eliminată, iOS ar putea decide să reconecteze o scenă la o sesiune de scene în orice moment, de exemplu atunci când un utilizator aduce din nou în prim-plan o anumită scenă.

Cel mai important lucru pe care trebuie să-l faceți în sceneDidDisconnect(_:) este să eliminați orice resurse pe care nu aveți nevoie să le păstrați. Acestea pot fi date care sunt ușor de încărcat de pe disc sau din rețea sau alte date pe care le puteți recrea cu ușurință. De asemenea, este important să vă asigurați că păstrați orice date care nu pot fi recreate cu ușurință, cum ar fi, de exemplu, orice intrare pe care utilizatorul a furnizat-o într-o scenă și care s-ar aștepta să fie încă acolo atunci când se întoarce la o scenă.

Considerați o aplicație de procesare a textului care acceptă scene multiple. Dacă un utilizator lucrează într-o scenă, după care o fundal pentru a face niște cercetări pe Safari și pentru a-și schimba muzica în Spotify, acesta s-ar aștepta neapărat ca toată munca sa să existe în continuare în aplicația de procesare a textului, chiar dacă iOS ar fi deconectat scena aplicației de procesare a textului pentru o perioadă de timp. Pentru a realiza acest lucru, aplicația trebuie să rețină datele necesare și ar trebui să codifice starea actuală a aplicației într-un obiect NSUserActivity care poate fi citit ulterior în scene(_:willConnectTo:options:) atunci când scena este reconectată.

Din moment ce acest flux de lucru de conectare, deconectare și reconectare a scenelor va separa aplicațiile bune de cele grozave, haideți să aruncăm o privire la modul în care puteți implementa restaurarea stării în aplicația dumneavoastră.

Realizarea unei configurări suplimentare a scenei

Există mai multe motive pentru care trebuie să efectuați o configurare suplimentară atunci când o scenă este configurată. S-ar putea să trebuiască să deschideți un URL, să gestionați o cerere de transfer sau să restaurați starea. În această secțiune, mă voi concentra mai ales pe restaurarea stării, deoarece acesta este probabil cel mai complex scenariu pe care s-ar putea să trebuiască să îl gestionați.

Restabilirea stării începe atunci când scena dvs. se deconectează și se apelează sceneDidDisconnect(_:). În acest moment, este important ca aplicația dvs. să aibă deja configurată o stare care poate fi restaurată ulterior. Cel mai bun mod de a face acest lucru este să folosiți NSUserActivity în aplicația dumneavoastră. Dacă folosiți NSUserActivity pentru a susține Handoff, Siri Shortcuts, indexarea Spotlight și altele, nu aveți mult de lucru în plus de făcut. Dacă nu utilizați încă NSUserActivity, nu vă faceți griji. O activitate simplă a utilizatorului ar putea arăta cam așa:

let activity = NSUserActivity(activityType: "com.donnywals.DocumentEdit")activity.userInfo = 

Rețineți că această activitate a utilizatorului nu este structurată așa cum o recomandă Apple, este un exemplu foarte sumar, menit să ilustreze restaurarea stării. Pentru un ghid complet cu privire la NSUserActivity, vă recomand să aruncați o privire pe documentația Apple pe această temă.

Când vine momentul în care trebuie să furnizați o activitate utilizator care poate fi restaurată ulterior, sistemul apelează metoda stateRestorationActivity(for:) pe SceneDelegate dvs. Rețineți că această metodă nu face parte din șablonul implicit

func stateRestorationActivity(for scene: UIScene) -> NSUserActivity? { return scene.userActivity}

Făcând acest lucru, asociază activitatea utilizatorului activă în acel moment pentru o scenă cu sesiunea scenei. Rețineți că, ori de câte ori o scenă este deconectată, UISceneSession care deține UIScene nu este eliminată pentru a permite sesiunii să se reconecteze la o scenă. Când se întâmplă acest lucru, scene(_:willConnectTo:options:) este apelat din nou. În această metodă, aveți acces la UISceneSession care deține UIScene, astfel încât să puteți citi stateRestorationActivity al sesiunii și să restaurați starea aplicației, după cum este necesar:

if let activity = session.stateRestorationActivity, activity.activityType == "com.donnywals.DocumentEdit", let documentId = activity.userInfo as? String { // find document by ID // create document viewcontroller and present it}

Desigur, detaliile fine ale acestui cod vor varia în funcție de aplicația dumneavoastră, dar ideea generală ar trebui să fie clară.

Dacă se așteaptă ca UISceneSession dvs. să gestioneze un URL, puteți inspecta urlContexts al obiectului connectionOptions pentru a găsi URL-uri pe care scena dvs. ar trebui să le deschidă și informații despre modul în care aplicația dvs. ar trebui să facă acest lucru:

for urlContext in connectionOptions.urlContexts { let url = urlContext.url let options = urlContext.options // handle url and options as needed}

Obiectul options va conține informații despre dacă scena dvs. ar trebui să deschidă URL-ul pe loc, ce aplicație a solicitat deschiderea acestui URL și alte metadate despre cerere.

Bazele restaurării stării în iOS 13 cu SceneDelegate sunt surprinzător de simple, mai ales că este construit pe NSUserActivity, ceea ce înseamnă că o mulțime de aplicații nu vor trebui să facă prea multă muncă pentru a începe să suporte restaurarea stării pentru scenele lor.

Rețineți că, dacă doriți să aveți suport pentru mai multe scene pentru aplicația dvs. pe iPadOS, restaurarea scenei este deosebit de importantă, deoarece iOS ar putea deconecta și reconecta scenele dvs. atunci când acestea trec din prim-plan în fundal și înapoi. Mai ales dacă aplicația dvs. permite unui utilizator să creeze sau să manipuleze obiecte într-o scenă, un utilizator nu s-ar aștepta ca munca sa să dispară dacă mută o scenă în fundal pentru un moment.

În rezumat

În acest articol de blog, ați învățat multe. Ați aflat ce roluri îndeplinesc AppDelegate și SceneDelegate în iOS 13 și cum arată ciclurile lor de viață. Acum știți că AppDelegate este responsabil de reacția la evenimentele de la nivelul aplicației, cum ar fi, de exemplu, lansarea aplicației. SceneDelegate este responsabil pentru evenimentele legate de ciclul de viață al scenei. De exemplu, crearea scenei, distrugerea și restaurarea stării unui UISceneSession. Cu alte cuvinte, principalul motiv pentru care Apple a adăugat UISceneDelegate în iOS 13 a fost acela de a crea un punct de intrare bun pentru aplicațiile cu mai multe ferestre.

După ce ați învățat despre elementele de bază ale UISceneDelegate, ați văzut un exemplu foarte simplu despre cum arată restaurarea stării în iOS 13 cu UISceneSession și UIScene. Desigur, există mult mai multe de învățat despre modul în care se comportă aplicația dvs. atunci când un utilizator generează mai multe UISceneSession pentru aplicația dvs. și despre modul în care aceste scene ar putea fi nevoite să rămână sincronizate sau să partajeze date.

Dacă doriți să aflați mai multe despre suportul pentru ferestre multiple pentru aplicația dvs. iPad (sau pentru aplicația dvs. macOS), nu uitați să consultați articolul meu Adăugarea suportului pentru ferestre multiple la aplicația dvs. iPadOS. Mulțumesc pentru lectură și nu ezitați să mă contactați pe Twitter dacă aveți întrebări sau feedback pentru mine.

Rămâneți la curent cu buletinul meu informativ săptămânal

Practical Combine

Învățați tot ce trebuie să știți despre Combine și cum îl puteți folosi în proiectele dvs. cu noua mea carte Practical Combine. Veți primi treisprezece capitole, un loc de joacă și o mână de exemple de proiecte pentru a vă ajuta să începeți să lucrați cu Combine cât mai repede cu putință.

Cartea este disponibilă ca descărcare digitală pentru doar 29,99 dolari!

Obțineți Practical Combine

.

Lasă un răspuns

Adresa ta de email nu va fi publicată.