iPadOS 13 was launched during WWDC 2019. Finally, the iPad got a separate OS.
Introduction of Multiple Window Support in iPadOS is a game-changing move. It allows us to open multiple instances of an application at the same time.
This awesome feature is incredibly useful when it comes to viewing multiple messages, emails or comparing notes and map routes. I believe Multiple Window Support would give rise to interesting multiplayer games really soon!
Our Goal For Today
- Knowing how the application lifecycle changes for Multiple Window Support
- A bird’s eye view at UIScene API
- Implementing Multiple Window Support in two different ways:
- By User Input
- By Drag And Drop
Without wasting any more time, let’s get started.
Enabling Multi-Window Support
It’s easy, just jump into the Project Navigator | General Settings and ensure that Support Multiple Window checkbox is enabled.
Once this is done, Multiple Window Support boolean property is set in the info.plist
. Before we dig deep into the API changes and implementation let’s address the elephant in the room:
Multiple Window Support is NOT Split Screen Support.
Split Screen Support was introduced in iOS 9 to allow viewing different apps in one window. Multiple Window Support allows viewing multiple instances of one app.
Changes In AppDelegate And App Lifecycle
Multi-Window Support has brought in major changes to the AppDelegate class. It is much lighter now. All the heavy weight lifting part is done by the SceneDelegate class. If you peek into the AppDelegate in any iOS 13 Xcode Project, you’ll see it has very few methods.
UIApplicationDelegate
is not notified when the application goes and comes from the background in iOS 13 and above.
A newly introduced protocol UIWindowSceneDelegate
handles the notifications across multiple windows of an application.
The following properties of the UIApplication
class are now deprecated:
statusBarStyle
statusBarHidden
statusBarOrientation
open(_:options:completionHandler:)
keyWindows
Thanks to Multi-Window Support, now windows are scenes! So everything you see in the App Switcher is a separate scene starting iPadOS 13.
UIScene API: A Bird’s eye view
Multiple Window Support uses UIScene
API under the hood. The two most essential classes of this API are:
UIWindowScene
– This is responsible for managing multiple windows of an application.UISceneSession
– This represents a persisted state of the scene. Multiple UIScenes store there specific information such as role, user info with the Scene Session.
NSUserActivity
is used to capture the state of a scene. This state is used to restore a previously used scene or create a new scene with the current viewing content.
If the above information didn’t make sense to you, don’t freak out! A hands on implementation in the next section will definitely make things clearer.
Implementation
We’ll be creating a simple iPadOS Application that displays an image.
Let’s start by adding a button that creates a new scene/window of the application.
Creating A New Scene On User Click
The following code creates a new Scene on user input:
let activity = NSUserActivity(activityType: VCActivityType) UIApplication.shared.requestSceneSessionActivation(nil, userActivity: activity, options: nil, errorHandler: nil)
requestSceneSessionActivation
is responsible for activating an existing scene or creating a new scene!
Here’s a screengrab from the iPad Application.
This was straight. Now let’s hop onto the Drag And Drop mechanism and see what needs to be done there.
Creating a New Scene Using Drag And Drop
We’ll implement the Drag interaction on the ImageView.
For that, we need to conform to the UIDragInteractionDelegate
protocol and set it’s iteraction on the UIImageView
instance.
photo?.isUserInteractionEnabled = true photo?.addInteraction(UIDragInteraction(delegate: self))
Implementing Drag Function
extension ViewController : UIDragInteractionDelegate{ func dragInteraction(_ interaction: UIDragInteraction, itemsForBeginning session: UIDragSession) -> [UIDragItem] { if let imageView = interaction.view as? UIImageView { guard let image = imageView.image else { return [] } let provider = NSItemProvider(object: image) let userActivity = NSUserActivity(activityType: VCActivityType) provider.registerObject(userActivity, visibility: .all) let item = UIDragItem(itemProvider: provider) print("here...") return [item] } return [] } }
Lastly, we need to add the activity type key in the info.plist
file:
Our iPad Application is now ready for multi-window support via dragging.
We’ve set CIFilters
on the images to compare the same image across different filters.
There’s more! In the next parts, we’ll deal with State Restoration and Syncing data across multiple windows in iPadOS.
The full source code of this tutorial is available in the Github Repository.