Understanding the iOS 13 Scene Delegate

Published by donnywals on October 28, 2019October 28, 2019

Amikor új projektet hoz létre az Xcode 11-ben, észrevehet valamit, amit eddig nem látott. Ahelyett, hogy csak egy AppDelegate.swift fájlt, egy ViewController.swift, egy storyboardot és néhány más fájlt hozna létre, az Xcode mostantól egy új fájlt hoz létre számodra; a SceneDelegate.swift fájlt. Ha még sosem láttad ezt a fájlt, elég zavaros lehet megérteni, hogy mi is ez, és hogyan kell használnod ezt az új scene delegate-et az alkalmazásodban.

Az e heti blogbejegyzés végére tudni fogod:

  • Mire szolgál a scene delegate.
  • Hogyan tudod hatékonyan implementálni a scene delegate-et.
  • Miért fontos része a scene delegate az iOS 13-nak.

Vágjunk bele, rendben?

Az új Xcode projektsablon vizsgálata

Amikor új Xcode projektet hozunk létre, lehetőségünk van kiválasztani, hogy a SwiftUI-t vagy a Storyboards-t szeretnénk használni. Függetlenül attól, hogy itt mit választasz, az Xcode egy újfajta projektsablont fog generálni, amelyre építhetsz. A SceneDelegate.swift és AppDelegate.swift fájlokat a következő részben fogjuk közelebbről megvizsgálni, egyelőre az a fontos, hogy megértsd, hogy az Xcode létrehozta neked ezeket a fájlokat.

A két delegate fájlon kívül az Xcode még egy kicsit finomabb dolgot is csinál. Nézd meg alaposan a Info.plist fájlodat. Látnia kell egy új, Application Scene Manifest nevű kulcsot, amelynek tartalma az alábbi képhez hasonló:

Képernyőkép az Info.plist fájl scene manifesztjéről

Képernyőkép az Info.plist fájl scene manifesztjéről

Ez a scene manifeszt egy nevet és egy delegált osztályt ad meg a jelenetéhez. Vegyük észre, hogy ezek a tulajdonságok egy tömbhöz tartoznak (Application Session Role), ami arra utal, hogy több konfiguráció is lehet a Info.plistben. Egy sokkal fontosabb kulcs, amelyet talán már észrevett a fenti képernyőképen, a Enable Multiple Windows. Ez a tulajdonság alapértelmezés szerint NO értékre van beállítva. Ha ezt a tulajdonságot YES-re állítod, akkor a felhasználók több ablakot is megnyithatnak az alkalmazásodból iPadOS-en (vagy akár macOS-en). Az, hogy egy iOS-alkalmazás több ablakát egymás mellett tudjuk futtatni, óriási különbség az eddigi egyetlen ablakos környezethez képest, és a több ablak lehetősége az egész oka annak, hogy az alkalmazásunk életciklusát mostantól nem egy, hanem két helyen tartjuk fenn.

Nézzük meg közelebbről a AppDelegate-et és a SceneDelegate-et, hogy jobban megértsük, hogyan működik együtt ez a két delegált, hogy lehetővé tegye a több ablak támogatását.

Az AppDelegate és a SceneDelegate szerepének megértése

Ha az iOS 13 előtt készítettél alkalmazásokat, valószínűleg úgy ismered a AppDelegate-t, mint az egyetlen helyet, amely nagyjából mindent elvégez az alkalmazásod indításával, előtérbe állításával, háttérbe állításával kapcsolatban, és még annál is többet. Az iOS 13-ban az Apple a AppDelegate feladatainak egy részét a SceneDelegate-be helyezte át. Vessünk egy rövid pillantást mind a két fájlra.

AppDelegate felelősségei

A AppDelegate továbbra is az alkalmazás fő belépési pontja az iOS 13-ban. Az Apple a AppDelegate metódusokat számos alkalmazásszintű életcikluseseményhez hívja meg. Az Apple alapértelmezett sablonjában három olyan metódust találsz, amelyeket az Apple fontosnak tart a használatukhoz:

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

Ezek a metódusok némi kommentárt tartalmaznak, amely valóban elég részletesen leírja, hogy megértsük, mit csinálnak. De azért menjünk át rajtuk gyorsan.

Amikor az alkalmazásodat éppen csak elindítottad, a func application(_:didFinishLaunchingWithOptions:) -> Bool meghívásra kerül. Ez a metódus az alkalmazás beállításának elvégzésére szolgál. Az iOS 12-ben vagy korábban ezt a metódust használhattad egy UIWindow objektum létrehozására és konfigurálására, és egy UIViewController példányt rendelhettél az ablakhoz, hogy megjelenjen.

Ha az alkalmazásod jeleneteket használ, a AppDelegate már nem felelős ezért. Mivel az alkalmazásodnak most már több ablak, vagy UISceneSession aktív lehet, nincs sok értelme egyetlen ablak objektumot kezelni a AppDelegate-ben.

A func application(_:configurationForConnecting:options:) -> UISceneConfigurationA func application(_:configurationForConnecting:options:) -> UISceneConfiguration meghívásra kerül, amikor az alkalmazásodnak új jelenetet, vagy ablakot kell szolgáltatnia az iOS számára a megjelenítéshez. Vegye figyelembe, hogy ezt a metódust nem hívja meg az alkalmazás kezdeti indításakor, csak az új jelenetek megszerzéséhez és létrehozásához hívja meg. A több jelenet létrehozását és kezelését egy későbbi blogbejegyzésben mélyebben megvizsgáljuk.

A AppDelegate sablon utolsó metódusa a func application(_:didDiscardSceneSessions:). Ez a metódus akkor hívódik meg, amikor a felhasználó eldob egy jelenetet, például a multitasking ablakban történő elhúzással, vagy ha ezt programozottan teszi. Ha az alkalmazásod nem fut, amikor a felhasználó ezt megteszi, akkor ez a metódus minden eldobott jelenethez meghívásra kerül röviddel a func application(_:didFinishLaunchingWithOptions:) -> Bool meghívása után.

Ezeken az alapértelmezett metódusokon kívül a AppDelegate még használható URL-ek megnyitására, memóriafigyelmeztetések fogására, az alkalmazásod befejezésének érzékelésére, arra, hogy az eszköz órája jelentősen megváltozott-e, annak érzékelésére, hogy a felhasználó regisztrált-e távoli értesítésekre és így tovább.

Tipp:
Fontos megjegyezni, hogy ha jelenleg a AppDelegate-t használja az alkalmazás állapotsorának megjelenésének kezelésére, akkor az iOS 13-ban néhány változtatást kell végrehajtania. Számos állapotsorral kapcsolatos metódus elavult az iOS 13-ban.

Most, hogy jobb képet kaptunk arról, hogy mik a AppDelegate új feladatai, nézzük meg az új SceneDelegate-et.

SceneDelegate felelősségei

Ha a AppDelegate-et tekintjük az alkalmazás életciklusáért felelős objektumnak, a SceneDelegate felelős azért, ami a képernyőn megjelenik; a jelenetekért vagy ablakokért. Mielőtt folytatnánk, határozzunk meg néhány jelenetekkel kapcsolatos szókincset, mert nem minden kifejezés jelenti azt, amit gondolnánk, hogy jelent.

Ha jelenetekkel foglalkozunk, azt, ami a felhasználó számára ablaknak tűnik, valójában UIScene-nak hívják, amelyet egy UISceneSession kezel. Tehát amikor ablakokra hivatkozunk, akkor valójában UISceneSession objektumokra utalunk. Igyekszem a blogbejegyzés során a lehető legnagyobb mértékben ragaszkodni ehhez a terminológiához.

Most, hogy egy oldalon állunk, nézzük meg a SceneDelegate.swift fájlt, amelyet az Xcode létrehozott, amikor létrehozta a projektünket.

A SceneDelegate.swift fájlban alapértelmezés szerint számos módszer található:

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

Ezeknek a módszereknek nagyon ismerősnek kell tűnniük, ha ismeri az iOS 13 előtt létező AppDelegate-et. Nézzük először a scene(_:willConnectTo:options:)-et, ez a metódus tűnik valószínűleg a legkevésbé ismerősnek számodra, és ez az első meghívott metódus egy UISceneSession életciklusában.

A scene(_:willConnectTo:options:) alapértelmezett implementációja létrehozza a kezdeti tartalmi nézetet (ContentView, ha a SwiftUI-t használod), létrehoz egy új UIWindow-t, beállítja az ablak rootViewController-jét, és ezt az ablakot teszi a kulcsablakká. Úgy gondolhatsz erre az ablakra, mint az ablakra, amelyet a felhasználó lát. Ez sajnos nem így van. Az ablakok már az iOS 13 előtt is léteztek, és azt a nézetablakot jelentik, amelyben az alkalmazásod működik. Tehát a UISceneSession vezérli a látható ablakot, amit a felhasználó lát, az általad létrehozott UIWindow pedig az alkalmazásod konténernézetét.

A kezdeti nézetek beállítása mellett a scene(_:willConnectTo:options:) segítségével visszaállíthatod a jelenet felhasználói felületét, ha a jeleneted a múltban megszakadt a kapcsolat. Például azért, mert a háttérbe került. A connectionOptions objektumot is kiolvashatja, ha a jelenete egy HandOff kérés miatt jött létre, vagy esetleg egy URL megnyitásához. Ennek módját később ebben a blogbejegyzésben mutatom meg.

Amikor a jelenete csatlakozott, a következő módszer a jelenet életciklusában a sceneWillEnterForeground(_:). Ez a metódus akkor hívódik meg, amikor a jeleneted átveszi a színpadot. Ez lehet, amikor az alkalmazásod átvált a háttérből az előtérbe, vagy ha most válik először aktívvá. Ezután a sceneDidBecomeActive(_:) meghívása következik. Ez az a pont, amikor a jeleneted be van állítva, látható és készen áll a használatra.

Amikor az alkalmazásod a háttérbe kerül, a sceneWillResignActive(_:) és a sceneDidEnterBackground(_:) meghívásra kerül. Ezekre a metódusokra most nem térek ki, mivel a céljuk minden alkalmazásnál más és más, és az Xcode sablonban található megjegyzések elég jól elmagyarázzák, hogy mikor hívják ezeket a metódusokat. Valójában biztos vagyok benne, hogy magad is ki tudod találni, hogy mikor hívják ezeket a metódusokat.

Egy sokkal érdekesebb metódus a sceneDidDisconnect(_:). Amikor a jeleneted a háttérbe kerül, az iOS úgy dönthet, hogy megszakítja a kapcsolatot és kiüríti a jelenetedet, hogy felszabadítsa az erőforrásokat. Ez nem azt jelenti, hogy az alkalmazásodat megölték vagy nem fut többé, egyszerűen csak azt jelenti, hogy az ennek a metódusnak átadott jelenet már nem aktív, és leválasztja a munkamenetét.

Megjegyezzük, hogy maga a munkamenet sem feltétlenül kerül eldobásra, az iOS bármikor dönthet úgy, hogy újra csatlakoztat egy jelenetet a jelenet munkamenetéhez, például amikor a felhasználó ismét előtérbe hoz egy adott jelenetet.

A legfontosabb dolog, amit a sceneDidDisconnect(_:)-ben kell tenned, hogy eldobsz minden olyan erőforrást, amit nem kell megtartanod. Ezek lehetnek olyan adatok, amelyek könnyen betölthetők a lemezről vagy a hálózatról, vagy más adatok, amelyeket könnyen újra előállíthatunk. Az is fontos, hogy minden olyan adatot megtartson, amelyet nem lehet könnyen újra létrehozni, mint például a felhasználó által egy jelenetben megadott minden olyan bemenetet, amelyről elvárható, hogy még mindig ott legyen, amikor visszatér a jelenethez.

Gondoljon egy olyan szövegfeldolgozó alkalmazásra, amely támogatja a több jelenetet. Ha egy felhasználó egy jelenetben dolgozik, majd a háttérben kutat a Safariban, és megváltoztatja a zenéjét a Spotifyban, akkor feltétlenül elvárná, hogy az összes munkája továbbra is megmaradjon a szövegfeldolgozó alkalmazásban, még akkor is, ha az iOS esetleg egy időre lekapcsolta a szövegfeldolgozó alkalmazás jelenetét. Ehhez az alkalmazásnak meg kell tartania a szükséges adatokat, és az alkalmazás aktuális állapotát egy NSUserActivity objektumba kell kódolnia, amely később scene(_:willConnectTo:options:)-ben olvasható, amikor a jelenet újra csatlakozik.

Mivel a jelenetek csatlakoztatásának, szétkapcsolásának és újrakapcsolásának ez a munkafolyamata fogja elválasztani a jó alkalmazásokat a nagyszerűektől, nézzük meg, hogyan valósíthatjuk meg az állapot visszaállítását az alkalmazásunkban.

Kiegészítő jelenetbeállítás elvégzése

Egy jelenet beállításakor több oka is lehet annak, hogy kiegészítő beállításokat kell elvégeznünk. Előfordulhat, hogy meg kell nyitni egy URL-t, kezelni kell egy Handoff-kérést vagy vissza kell állítani az állapotot. Ebben a szakaszban leginkább az állapot helyreállítására fogok koncentrálni, mivel valószínűleg ez a legösszetettebb forgatókönyv, amit kezelni kell.

Az állapot helyreállítása akkor kezdődik, amikor a jelenet lekapcsolódik, és a sceneDidDisconnect(_:) meghívásra kerül. Ezen a ponton fontos, hogy az alkalmazásodnak már legyen egy olyan állapota, amelyet később vissza tudsz állítani. Ennek legjobb módja, ha az NSUserActivity-t használod az alkalmazásodban. Ha a NSUserActivity-t használja a Handoff, a Siri Shortcuts, a Spotlight indexelés és egyebek támogatására, akkor nincs sok extra munkája. Ha még nem használja a NSUserActivity-et, ne aggódjon. Egy egyszerű felhasználói tevékenység egy kicsit így nézhet ki:

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

Megjegyezzük, hogy ez a felhasználói tevékenység nem úgy van felépítve, ahogy az Apple ajánlja, ez egy nagyon puszta példa, amely az állapot helyreállítását hivatott szemléltetni. A NSUserActivity teljes útmutatójához azt ajánlom, hogy nézze meg az Apple dokumentációját ebben a témában.

Amikor eljön az ideje, hogy olyan felhasználói tevékenységet adjon meg, amely később visszaállítható, a rendszer meghívja a stateRestorationActivity(for:) metódust a SceneDelegate-jén. Vegye figyelembe, hogy ez a metódus nem része az alapértelmezett sablonnak

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

Ezzel a módszerrel a jelenet aktuálisan aktív felhasználói tevékenységét társítja a jelenet munkamenetéhez. Ne feledje, hogy amikor egy jelenet lekapcsolódik, a UISceneSession, amely a UIScene tulajdonosa, nem kerül elvetésre, hogy a munkamenet újra csatlakozhasson egy jelenethez. Amikor ez megtörténik, a scene(_:willConnectTo:options:) újra meghívásra kerül. Ebben a módszerben hozzáférhetünk a UIScene-ot birtokló UISceneSession-hez, így szükség esetén kiolvashatjuk a munkamenet stateRestorationActivity-ját, és visszaállíthatjuk az alkalmazás állapotát:

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}

A kód finom részletei természetesen az alkalmazásunktól függően változnak, de az általános elképzelésnek világosnak kell lennie.

Ha a UISceneSession-ednek URL-t kell kezelnie, akkor megvizsgálhatod a connectionOptions objektum urlContexts-jét, hogy megtaláld azokat az URL-eket, amelyeket a jelenetednek meg kell nyitnia, és információt arról, hogy az alkalmazásodnak hogyan kell ezt megtennie:

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

A options objektum tartalmaz információt arról, hogy a jelenetednek meg kell-e nyitnia az URL-t a helyén, milyen alkalmazás kérte az URL megnyitását, és egyéb metaadatokat a kérésről.

Az állapot-visszaállítás alapjai az iOS 13-ban a SceneDelegate segítségével meglepően egyszerűek, különösen mivel a NSUserActivity-re épül, ami azt jelenti, hogy sok alkalmazásnak nem kell túl sok munkát végeznie ahhoz, hogy elkezdje támogatni a jelenetek állapot-visszaállítását.

Ne feledjük, hogy ha több jelenetet szeretnénk támogatni az alkalmazásunk számára az iPadOS-en, a jelenet-visszaállítás különösen fontos, mivel az iOS megtörténhet, hogy a jeleneteket szétkapcsolja és újra összekapcsolja, amikor azok az előtérből a háttérbe és vissza váltanak. Különösen, ha az alkalmazásod lehetővé teszi a felhasználó számára, hogy objektumokat hozzon létre vagy manipuláljon egy jelenetben, a felhasználó nem számíthat arra, hogy a munkája eltűnik, ha egy jelenetet egy pillanatra a háttérbe helyez.

Összefoglalva

Ebben a blogbejegyzésben sok mindent megtudtál. Megtudtad, hogy a AppDelegate és a SceneDelegate milyen szerepet tölt be az iOS 13-ban, és hogyan néz ki az életciklusuk. Most már tudod, hogy a AppDelegate felelős az alkalmazásszintű eseményekre való reagálásért, mint például az alkalmazás indítása. A SceneDelegate a jelenet életciklusával kapcsolatos eseményekért felelős. Például egy UISceneSession jelenet létrehozása, megsemmisítése és állapotának visszaállítása. Más szóval, az Apple fő oka, hogy az UISceneDelegate-t hozzáadta az iOS 13-hoz, az volt, hogy egy jó belépési pontot hozzon létre a többablakos alkalmazások számára.

Az UISceneDelegate alapjainak megismerése után láttál egy nagyon egyszerű példát arra, hogyan néz ki az állapot helyreállítása az iOS 13-ban a UISceneSession és a UIScene segítségével. Természetesen sokkal többet kell megtudnod arról, hogyan viselkedik az alkalmazásod, amikor a felhasználó több UISceneSession-et hoz létre az alkalmazásodhoz, és hogyan kell ezeknek a jeleneteknek esetleg szinkronban maradniuk vagy adatokat megosztaniuk.

Ha többet szeretnél megtudni a több ablak támogatásáról az iPad alkalmazásodban (vagy a macOS alkalmazásodban), mindenképpen nézd meg a Több ablak támogatásának hozzáadása az iPadOS alkalmazásodhoz című írásomat. Köszönöm, hogy elolvastad, és ne habozz, ha bármilyen kérdésed vagy visszajelzésed van számomra a Twitteren.

Legyen naprakész a heti hírlevelemmel

Practical Combine

Tanulj meg mindent, amit a Combine-ról tudni kell, és hogyan használhatod a projektjeidben az új Practical Combine című könyvemből. Tizenhárom fejezetet, egy játszóteret és egy maroknyi mintaprojektet kap, hogy minél hamarabb elkezdhesse használni a Combine-t.

A könyv digitális letöltésként mindössze 29,99 $-ért kapható!

Get Practical Combine

Vélemény, hozzászólás?

Az e-mail-címet nem tesszük közzé.