WWDC 19
Introducing Multiple Windows on iPad
The user should be able to do everything from a single window if they want to – if your app *requires* using multiple windows, then something is wrong
In most apps there will be only one kind of window – you should be able to move from every starting point in each window to every other place in the app
An example of an app that has different kinds of windows is Mail, where you can open a new window for a composed message, but it’s a window dedicated to that new message and you can’t get back to the list inside it
Another example is Messages where you can pull out a conversation to a new window, and it’s a window only for that conversation
In every document-based app (e.g. Pages) the user may have the expectation that they can work with different documents in different windows
In non-document apps like Maps or Calendar you will also often want to be able to do two different things, see two different contexts or scenarios at the same time, and now you can do that by opening two windows
Ways to create a new window:
- in the app expose, there’s a “+” button in the corner that creates new windows
- when you have the app open and you drag its icon from the dock to the side, it creates a second window
- in a lot of places in your app it might make sense to hold & drag an element like a tab to the side and create a window from it, but for that you need to implement drag & drop. Whenever you have a place where the user can drag & drop something and it would make sense to make a window from the dragged thing, it should be possible.
- a common example is any master-detail view like Mail, where you can drag any list row (email) out to a new window
- explicit action like an “Open in New Window” action in a context menu
UIWindowScene
:
- contains the user interface (
UIWindow
) of a single “window” - it’s created on demand for you and destroyed when unused
UISceneSession
:
- represents the state in which the UI in a given window is or was last time when it was persisted
Difference between scene and session: scenes are released to save memory, and when the user sees 4 windows of the app in the switcher, it’s possible that only one of them is currently active in memory – the others are stored as persisted sessions, and when selected in the switcher, they will be “unfreezed” and assigned a scene again
UIApplicationDelegate
gets split up:
UIApplicationDelegate
still processes lifecycle events about the whole appUIApplication
still stores the state of the whole app- any lifecycle events that concern only a specific scene are instead handled by
UIWindowSceneDelegate
usingUIWindowScene
&UISceneSession
- methods like application will enter foreground/background,
applicationDidFinishLaunching
, application open URL etc. go to scene delegate
New state restoration API based on NSUserActivity
:
UISceneSession.stateRestorationActivity
UISceneSession.persistentIdentifier
– can be used to integrate with custom state restoration codeUISceneSession.userInfo
– for storing simple settings like what element is currently selected or what mode an element is inSceneDelegate
:stateRestorationActivity(for scene:) -> NSUserActivity?
SceneDelegate
:scene(_: willConnectTo session: options:)
– initializer like inUIApplicationDelegate
, options includeuserActivities
for activities passed e.g. from HandoffAppDelegate
:application(_: didDiscardSceneSessions:)
– use this if needed to clean up saved data for a session when it gets destroyed
How to implement multi-window support:
- 1. Check “Supports multiple windows” in the target settings
- 2. Set up Scene Configuration / Application Session Role list in
Info.plist
with one or more session roles (name of delegate class and storyboard file) - 3. Implement the two necessary delegate methods in
UIWindowSceneDelegate
- 4. If needed, add ways to create new windows, e.g. using drag & drop or custom buttons
APIs in UIApplication
to programatically manage sessions:
requestSceneSessionActivation(_:userActivity:options:errorHandler:)
– creates a new scene for a given user activity, or activates an existing sessionrequestSceneSessionRefresh(_:)
– requests an update to a scenerequestSceneSessionDestruction(_:options:errorHandler:)
– deletes a scene (you can select one of 3 animations)
Be prepared for this change to check some assumptions you might have made, that there will be only one instance of a given VC at a time etc… :)
Watch out for global variables, singletons and other shared data
Keeping UserDefaults
up to date between scenes: use KVO observation on UserDefaults
UIApplication
statusBar*
and keyWindow
methods and open(_: options: completionHandler:)
callback are deprecated, replaced by equivalents on UIWindowScene
(statusBarManager
, interfaceOrientation
)
- this is even for apps that will not use multiple scenes