Pochopení delegáta scény v iOS 13

Vydal donnywals dne 28. října 201928. října 2019

Při vytváření nového projektu v Xcode 11 si možná všimnete něčeho, co jste dosud neviděli. Namísto pouhého vytvoření souboru AppDelegate.swift, ViewController.swift, storyboardu a několika dalších souborů pro vás nyní Xcode vytvoří nový soubor; soubor SceneDelegate.swift. Pokud jste tento soubor nikdy předtím neviděli, může být docela matoucí pochopit, co to je a jak máte tento nový delegát scény ve své aplikaci používat.

Na konci tohoto týdenního příspěvku na blogu budete vědět:

  • K čemu slouží delegát scény.
  • Jak můžete efektivně implementovat delegát scény.
  • Proč je delegát scény důležitou součástí systému iOS 13.

Přejděme rovnou k věci, ano?“

Prozkoumání nové šablony projektu Xcode

Kdykoli vytváříte nový projekt Xcode, máte možnost zvolit, zda chcete používat SwiftUI nebo Storyboards. Bez ohledu na vaši volbu zde Xcode vygeneruje nový druh šablony projektu, na které můžete stavět. Na soubory SceneDelegate.swift a AppDelegate.swift se blíže podíváme v další části, důležité je, abyste prozatím pochopili, že Xcode tyto soubory vytvořil za vás.

Kromě těchto dvou souborů delegátů dělá Xcode ještě něco trochu nenápadnějšího. Podívejte se pozorně na svůj soubor Info.plist. Měli byste vidět nový klíč s názvem Application Scene Manifest s obsahem podobným následujícímu obrázku:

Snímek manifestu scény souboru Info.plist

Snímek manifestu scény souboru Info.plist

Tento manifest scény určuje název a třídu delegáta pro vaši scénu. Všimněte si, že tyto vlastnosti patří do pole (Application Session Role), což naznačuje, že můžete mít ve své Info.plistkonfiguraci více konfigurací. Mnohem důležitějším klíčem, kterého jste si již možná všimli na obrázku výše, je Enable Multiple Windows. Tato vlastnost je ve výchozím nastavení nastavena na NO. Nastavení této vlastnosti na YES umožní uživatelům otevřít více oken vaší aplikace v systému iPadOS (nebo dokonce v systému macOS). Možnost spouštět více oken aplikace pro iOS vedle sebe je obrovský rozdíl oproti prostředí s jedním oknem, se kterým jsme dosud pracovali, a možnost mít více oken je celým důvodem, proč je nyní životní cyklus naší aplikace udržován na dvou místech místo jednoho.

Podívejme se blíže na AppDelegate a SceneDelegate, abychom lépe pochopili, jak tyto dva delegáty spolupracují, aby umožnily podporu více oken.

Poznání rolí AppDelegate a SceneDelegate

Pokud jste vytvářeli aplikace před systémem iOS 13, pravděpodobně znáte svůj AppDelegate jako jediné místo, které dělá v podstatě vše, co souvisí se spuštěním, popředím a pozadím vaší aplikace, a ještě něco navíc. V systému iOS 13 společnost Apple přesunula část povinností AppDelegate na SceneDelegate. Podívejme se stručně na každý z těchto dvou souborů.

OdpovědnostiAppDelegate

V systému iOS 13 je AppDelegate stále hlavním vstupním bodem aplikace. Společnost Apple volá metody AppDelegate pro několik událostí životního cyklu na úrovni aplikace. Ve výchozí šabloně Apple najdete tři metody, které Apple považuje za důležité, abyste je používali:

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

Tyto metody mají v sobě nějaký komentář, který vlastně popisuje jejich činnost dostatečně podrobně, abyste pochopili, co dělají. Ale i tak si je rychle projdeme.

Když je vaše aplikace právě spuštěna, zavolá se metoda func application(_:didFinishLaunchingWithOptions:) -> Bool. Tato metoda slouží k provedení nastavení aplikace. V systému iOS 12 nebo dřívějších verzích jste mohli tuto metodu použít k vytvoření a nastavení objektu UIWindow a přiřadit instanci UIViewController oknu, aby se zobrazilo.

Pokud vaše aplikace používá scény, AppDelegate již není za tuto činnost zodpovědná. Protože vaše aplikace nyní může mít aktivních více oken nebo UISceneSession, nemá příliš smysl spravovat objekt jednoho okna v AppDelegate.

Tento func application(_:configurationForConnecting:options:) -> UISceneConfiguration je volán vždy, když má vaše aplikace dodat novou scénu nebo okno, které má iOS zobrazit. Všimněte si, že tato metoda není volána při počátečním spuštění vaší aplikace, je volána pouze pro získání a vytvoření nových scén. Vytvářením a správou více scén se budeme hlouběji zabývat v některém z dalších příspěvků na blogu.

Poslední metodou v šabloně AppDelegate je func application(_:didDiscardSceneSessions:). Tato metoda je volána vždy, když uživatel scénu zavrhne, například tím, že ji v okně multitaskingu přejede pryč, nebo pokud tak učiníte programově. Pokud vaše aplikace není spuštěna, když to uživatel udělá, bude tato metoda zavolána pro každou zahozenou scénu krátce po zavolání func application(_:didFinishLaunchingWithOptions:) -> Bool.

Kromě těchto výchozích metod lze vaši AppDelegate ještě použít k otevírání adres URL, zachycení varování o nedostatku paměti, zjištění, kdy bude vaše aplikace ukončena, zda se výrazně změnily hodiny zařízení, zjištění, kdy se uživatel zaregistroval pro vzdálená oznámení, a další.

Tip:
Důležité je poznamenat, že pokud v současné době používáte AppDelegate ke správě vzhledu stavového řádku své aplikace, budete možná muset v systému iOS 13 provést některé změny. Několik metod souvisejících se stavovým řádkem bylo v systému iOS 13 zrušeno.

Teď, když máme lepší představu o tom, jaké jsou nové povinnosti vašeho AppDelegate, podívejme se na nový SceneDelegate.

Odpovědnosti delegáta scény

Pokud považujete AppDelegate za objekt, který je zodpovědný za životní cyklus vaší aplikace, SceneDelegate je zodpovědný za to, co se zobrazuje na obrazovce; za scény nebo okna. Než budeme pokračovat, zavedeme si slovní zásobu týkající se scén, protože ne každý termín znamená to, co si možná myslíte, že znamená.

Když se zabýváte scénami, to, co pro uživatele vypadá jako okno, se ve skutečnosti nazývá UIScene, které spravuje UISceneSession. Když tedy mluvíme o oknech, máme ve skutečnosti na mysli objekty UISceneSession. V průběhu tohoto příspěvku se budu snažit co nejvíce držet této terminologie.

Teď, když jsme na stejné vlně, se podívejme na soubor SceneDelegate.swift, který Xcode vytvořil při vytváření našeho projektu.

V souboru SceneDelegate.swift je ve výchozím nastavení několik metod:

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

Tyto metody by vám měly připadat velmi povědomé, pokud znáte AppDelegate, které existovaly před systémem iOS 13. Podívejme se nejprve na scene(_:willConnectTo:options:), tato metoda vám pravděpodobně připadá nejméně povědomá a je to první metoda volaná v životním cyklu UISceneSession.

Výchozí implementace scene(_:willConnectTo:options:) vytvoří vaše počáteční zobrazení obsahu (ContentView, pokud používáte SwiftUI), vytvoří nové UIWindow, nastaví rootViewController okna a udělá z tohoto okna klíčové okno. Toto okno si můžete představit jako okno, které vidí váš uživatel. Bohužel tomu tak není. Okna existují již před systémem iOS 13 a představují pohledové okno, ve kterém vaše aplikace pracuje. Takže UISceneSession řídí viditelné okno, které uživatel vidí, UIWindow, které vytvoříte, je kontejnerové zobrazení vaší aplikace.

Kromě nastavení počátečních zobrazení můžete pomocí scene(_:willConnectTo:options:) obnovit uživatelské rozhraní scény v případě, že se vaše scéna v minulosti odpojila. Například proto, že byla odeslána na pozadí. Můžete také přečíst objekt connectionOptions, abyste zjistili, zda byla vaše scéna vytvořena kvůli požadavku HandOff nebo třeba kvůli otevření adresy URL. Ukážu vám, jak to udělat později v tomto příspěvku na blogu.

Pokud se vaše scéna připojila, další metodou v životním cyklu vaší scény je sceneWillEnterForeground(_:). Tato metoda se zavolá, když vaše scéna převezme scénu. Může to být, když vaše aplikace přechází z pozadí do popředí, nebo pokud se právě stává poprvé aktivní. Dále se volá metoda sceneDidBecomeActive(_:). To je okamžik, kdy je vaše scéna nastavena, viditelná a připravená k použití.

Když vaše aplikace přejde do pozadí, jsou volány metody sceneWillResignActive(_:) a sceneDidEnterBackground(_:). Těmito metodami se nyní nebudu zabývat, protože jejich účel se u každé aplikace liší a komentáře v šabloně Xcode docela dobře vysvětlují, kdy jsou tyto metody volány. Vlastně jsem si jistý, že sami dokážete zjistit, kdy jsou tyto metody volány.

Zajímavější metodou je sceneDidDisconnect(_:). Kdykoli je vaše scéna odeslána na pozadí, může se systém iOS rozhodnout odpojit a vymazat scénu, aby uvolnil prostředky. To neznamená, že vaše aplikace byla zabita nebo již neběží, znamená to pouze, že scéna předaná této metodě již není aktivní a odpojí se od své relace.

Všimněte si, že samotná relace také nemusí být nutně zahozena, iOS se může kdykoli rozhodnout znovu připojit scénu k relaci scény, například když uživatel přivede konkrétní scénu znovu do popředí.

Nejdůležitější věcí, kterou je třeba udělat v sceneDidDisconnect(_:), je zahodit všechny prostředky, které nepotřebujete mít u sebe. Mohou to být data, která lze snadno načíst z disku nebo ze sítě, nebo jiná data, která lze snadno znovu vytvořit. Důležité je také zajistit, abyste si ponechali všechna data, která nelze snadno znovu vytvořit, jako například všechny vstupy, které uživatel zadal ve scéně a u kterých by očekával, že tam budou i po návratu do scény.

Přemýšlejte o aplikaci pro zpracování textu, která podporuje více scén. Pokud uživatel pracuje v jedné scéně, pak ji pozadí, aby provedl nějaký výzkum v Safari a změnil hudbu ve Spotify, naprosto by očekával, že veškerá jeho práce bude stále existovat v aplikaci pro zpracování textu, i když iOS mohl scénu aplikace pro zpracování textu na chvíli odpojit. Aby toho aplikace dosáhla, musí uchovávat potřebná data a měla by aktuální stav aplikace zakódovat do objektu NSUserActivity, který lze později přečíst v scene(_:willConnectTo:options:), když je scéna znovu připojena.

Protože tento pracovní postup připojování, odpojování a opětovného připojování scén bude oddělovat dobré aplikace od skvělých, podívejme se, jak můžete ve své aplikaci implementovat obnovení stavu.

Provádění dodatečného nastavení scény

Existuje několik důvodů, proč musíte při nastavování scény provádět dodatečné nastavení. Možná budete muset otevřít adresu URL, zpracovat požadavek na předání nebo obnovit stav. V této části se zaměřím hlavně na obnovení stavu, protože to je pravděpodobně nejsložitější scénář, který můžete muset řešit.

Obnovení stavu začíná, když se vaše scéna odpojí a zavolá se sceneDidDisconnect(_:). V tomto okamžiku je důležité, aby vaše aplikace již měla nastavený stav, který lze později obnovit. Nejlepším způsobem, jak toho dosáhnout, je použít ve své aplikaci NSUserActivity. Pokud používáte NSUserActivity pro podporu funkcí Handoff, Siri Shortcuts, indexování Spotlight a dalších, nemáte mnoho práce navíc. Pokud NSUserActivity zatím nepoužíváte, nezoufejte. Jednoduchá uživatelská aktivita může vypadat trochu takto:

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

Všimněte si, že tato uživatelská aktivita není strukturována tak, jak doporučuje společnost Apple, je to velmi holý příklad, který má ilustrovat obnovení stavu. Pro kompletní návod na NSUserActivity doporučuji podívat se do dokumentace společnosti Apple k tomuto tématu.

Když nastane čas, abyste poskytli uživatelskou aktivitu, kterou lze později obnovit, systém zavolá metodu stateRestorationActivity(for:) na vašem SceneDelegate. Všimněte si, že tato metoda není součástí výchozí šablony

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

Tímto způsobem spojíte aktuálně aktivní uživatelskou aktivitu pro scénu s relací scény. Nezapomeňte, že kdykoli je scéna odpojena, UISceneSession, který vlastní UIScene, není zahozen, aby se relace mohla znovu připojit ke scéně. Když k tomu dojde, zavolá se znovu scene(_:willConnectTo:options:). V této metodě máte přístup k UISceneSession, který vlastní UIScene, takže můžete přečíst stateRestorationActivity relace a podle potřeby obnovit stav aplikace:

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}

Podrobnosti tohoto kódu se samozřejmě budou lišit v závislosti na vaší aplikaci, ale obecná myšlenka by měla být jasná.

Pokud se od vaší UISceneSession očekává, že bude zpracovávat URL, můžete prozkoumat urlContexts objektu connectionOptions a najít URL, které by vaše scéna měla otevřít, a informace o tom, jak by to vaše aplikace měla udělat:

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

Objekt options bude obsahovat informace o tom, zda má vaše scéna otevřít URL na místě, jaká aplikace požadovala otevření této URL a další metadata o požadavku.

Základy obnovy stavu v systému iOS 13 pomocí objektu SceneDelegate jsou překvapivě jednoduché, zejména proto, že je postaven na objektu NSUserActivity, což znamená, že mnoho aplikací nebude muset udělat příliš mnoho práce, aby začaly podporovat obnovu stavu svých scén.

Mějte na paměti, že pokud chcete mít podporu pro více scén pro svou aplikaci v systému iPadOS, je obnova stavu scény obzvláště důležitá, protože systém iOS může vaše scény odpojovat a znovu připojovat při přepínání z popředí do pozadí a zpět. Zejména pokud vaše aplikace umožňuje uživateli vytvářet nebo manipulovat s objekty ve scéně, uživatel by neočekával, že jeho práce zmizí, pokud scénu na chvíli přesune na pozadí.

Shrnutí

V tomto příspěvku na blogu jste se dozvěděli mnoho nového. Dozvěděli jste se, jaké role plní AppDelegate a SceneDelegate v systému iOS 13 a jak vypadají jejich životní cykly. Nyní víte, že AppDelegate je zodpovědný za reakci na události na úrovni aplikace, jako je například spuštění aplikace. Funkce SceneDelegate je zodpovědná za události související s životním cyklem scény. Například vytvoření scény, její zničení a obnovení stavu UISceneSession. Jinými slovy, hlavním důvodem, proč společnost Apple přidala UISceneDelegate do systému iOS 13, bylo vytvoření dobrého vstupního bodu pro víceokenní aplikace.

Po seznámení se základy UISceneDelegate jste viděli velmi jednoduchý příklad toho, jak vypadá obnovení stavu v systému iOS 13 pomocí UISceneSession a UIScene. Samozřejmě se můžete dozvědět mnohem více o tom, jak se vaše aplikace chová, když uživatel pro vaši aplikaci vytvoří více UISceneSession, a o tom, jak tyto scény mohou muset zůstat synchronizované nebo sdílet data.

Pokud se chcete dozvědět více o podpoře více oken pro vaši aplikaci pro iPad (nebo aplikaci pro macOS), určitě si přečtěte můj příspěvek Přidání podpory více oken do vaší aplikace pro iPadOS. Děkuji za přečtení a neváhejte se na mě obrátit na Twitteru, pokud máte nějaké dotazy nebo zpětnou vazbu.

Sledujte můj týdenní zpravodaj

Praktický Combine

Vše, co potřebujete vědět o Combine a jak jej můžete použít ve svých projektech, se dozvíte z mé nové knihy Praktický Combine. Získáte třináct kapitol, hřiště a několik ukázkových projektů, které vám pomohou začít s Combine co nejdříve pracovat.

Kniha je k dispozici ke stažení v digitální podobě za pouhých 29,99 USD!

Získejte Praktický Combine

.

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna.