Dunfey · Hotel WWDC as data, est. 1983
Front desk everything
Years
Topics

2021 EssentialsSwiftUI & UI Frameworks

WWDC21 · 36 min · Essentials / SwiftUI & UI Frameworks

Take your iPad apps to the next level

Make even better iPad apps: Learn how you can adopt prominent scenes for uninterrupted, focused interactions. Help people stay engaged and fast with keyboard shortcuts and the keyboard shortcut interface. Explore how the latest in pointer enhancements can help your app boost productivity.

Watch at developer.apple.com ↗

Transcript all transcripts

Code shown on screen · 18 snippets

Build an "Open in New Window" action swift · at 4:56 ↗
let <#newSceneAction#> = UIWindowScene.ActivationAction({ _ in

    // Create the user activity that represents the new scene content.
    let userActivity = NSUserActivity(activityType: <#User Activity Type#>)

    // Return the activation configuration.
    return UIWindowScene.ActivationConfiguration(userActivity: userActivity)

})
Use an alternate action with UIWindowScene.ActivationAction swift · at 5:43 ↗
// Create an action to use when multiple scenes are not available.
let alternateAction = UIAction(title: <#Alternate Action Title#>,
                               image: <#Alternate Action Image#>,
                             handler: { _ in
    <#Perform Alternate Action#>
})

// Create the scene activation action with the alternate.
let newSceneAction = UIWindowScene.ActivationAction(alternate: alternateAction) { _ in

    // Create the user activity that represents the new scene content.
    let userActivity = NSUserActivity(activityType: <#Scene Activity Type#>)

    // Return the activation configuration.
    return UIWindowScene.ActivationConfiguration(userActivity: userActivity)
}
Present a scene from a collection view with a gesture swift · at 6:58 ↗
func collectionView(_ collectionView: UICollectionView,
                    sceneActivationConfigurationForItemAt indexPath: IndexPath,
                    point: CGPoint) -> UIWindowScene.ActivationConfiguration? {

    // Get the item's user activity.
    guard let itemActivity = <#User Activity#> else {
        // Return nil if item can’t be opened in a dedicated scene.
        return nil
    }

    // Return the activation configuration.
    return UIWindowScene.ActivationConfiguration(userActivity: itemActivity)
}
Present a scene from a custom view with a gesture swift · at 7:28 ↗
// Create an activation interaction.
let newSceneInteraction = UIWindowScene.ActivationInteraction { interaction, point in
    // Get the activity for specific point in view.
    guard let userActivity = <#User Activity#> else { return nil }

    // Return an activation configuration.
    return UIWindowScene.ActivationConfiguration(userActivity: userActivity)

} errorHandler: { error in
    // Present the content in another manner.
    <#Present Content#>
}

// Add interaction to the view.
<#View#>.addInteraction(newSceneInteraction)
Customize scene transition preview swift · at 8:53 ↗
// Create the activation configuration.
let itemActivity = NSUserActivity(activityType: <#User Activity Type#>)
let configuration = UIWindowScene.ActivationConfiguration(userActivity: itemActivity)

// If the cell has a subview to use as the preview, create the custom preview.
if let cell = collectionView.cellForItem(at: indexPath) as? <#Expected Cell Class#> {
    configuration.preview = UITargetedPreview(view: cell.<#Subview For Preview#>)
}

// Return the activation configuration.
return configuration
Save scene state swift · at 10:18 ↗
func stateRestorationActivity(for scene: UIScene) -> NSUserActivity? {
    guard let viewController = self.window?.rootViewController as? <#Expected View Controller Class#> else {
        return nil
    }

    let stateActivity = NSUserActivity(activityType: <#State Restoration Activity Type#>)

    stateActivity.addUserInfoEntries(from: [
        // Save content of a text field.
        <#Content Key#>: viewController.<#Text Field#>.text
    ])

    return stateActivity
}
Save scene state with interaction state swift · at 11:16 ↗
func stateRestorationActivity(for scene: UIScene) -> NSUserActivity? {
    guard let viewController = self.window?.rootViewController as? <#Expected View Controller Class#> else {
        return nil
    }

    let stateActivity = NSUserActivity(activityType: <#State Restoration Activity Type#>)

    stateActivity.addUserInfoEntries(from: [
        // Save content of a text field.
        <#Content Key#>: viewController.<#Text Field#>.text,

        // Save interaction state of a text field.
        <#Interaction State Key#>: viewController.<#Text Field#>.interactionState
    ])

    return stateActivity
}
Restore scene state swift · at 12:13 ↗
func scene(_ scene: UIScene, restoreInteractionState stateRestorationActivity: NSUserActivity) {
    guard let viewController = window?.rootViewController as? <#Expected View Controller Class#>,
          let userInfo = stateRestorationActivity.userInfo
    else { return }

    if let content = userInfo[<#Content Key#>] as? String {
        // Restore the content first.
        viewController.<#Text Field#>.text = content

        // Then, restore the text field’s interaction state.
        if let interactionState = userInfo[<#Interaction State Key#>] {
            viewController.<#Text Field#>.interactionState = interactionState
        }
    }
}
Restore scene state asynchronously swift · at 13:15 ↗
func scene(_ scene: UIScene, restoreInteractionState stateRestorationActivity: NSUserActivity) {
    guard let viewController = window?.rootViewController as? <#Expected View Controller Class#> else { return }

    // Request an extension.
    scene.extendStateRestoration()

    // Fetch content asynchronously.
    <#self.someAsyncFunction#> { result in
        <#Restore Content#>

        // Signal that state has been restored.
        scene.completeStateRestoration()
    }
}
Modify the main menu swift · at 17:15 ↗
override func buildMenu(with builder: UIMenuBuilder) {
    super.buildMenu(with: builder)
    
    // Ensure the builder is modifying the main menu.
    guard builder.system == .main else { return }
    
    // Use the builder to modify the main menu...
}
Add key commands to the main menu swift · at 17:37 ↗
// Create a menu with key commands.
let tabMenu = UIMenu(options: .displayInline, children: [
    UIKeyCommand(title: NSLocalizedString("New Tab", ...),
                 action: #selector(BrowserViewController.newTab(_:)),
                 input: "t",
                 modifierFlags: .command),
    UIKeyCommand(...)
])

// Insert tabMenu into the File menu.
builder.insertChild(tabMenu, atStartOfMenu: .file)
Add a custom menu category swift · at 18:19 ↗
// Create a "Bookmarks" menu.
let bookmarksMenu = UIMenu(title: NSLocalizedString("Bookmarks", ...),
                           children: [...])

// Insert the Bookmarks menu into the root menu, after View.
builder.insertSibling(bookmarksMenu, afterMenu: .view)

// Insert another menu into the Bookmarks menu.
let sortBookmarksMenu = UIMenu(...)
builder.insertChild(sortBookmarksMenu, atEndOfMenu: bookmarksMenu.identifier)
Customizing key command performability swift · at 22:38 ↗
override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
    if action == #selector(closeTab(_:)) {
        return !openTabs.isEmpty
    } else {
        return super.canPerformAction(action, withSender: sender)
    }
}
Customizing key command appearance swift · at 23:26 ↗
override func validate(_ command: UICommand) {
    if command.action == #selector(toggleBookmark(_:)) {
        if currentTab.isInBookmarks {
            command.title = NSLocalizedString("Add to Bookmarks", ...)
        } else {
            command.title = NSLocalizedString("Remove from Bookmarks", ...)
        }
    } else {
        return super.validate(command)
    }
}
Supporting multi-selection using UIBandSelectionInteraction swift · at 28:47 ↗
// Support multi-selection using UIBandSelectionInteraction.

let selectionInteraction = UIBandSelectionInteraction { [weak self] interaction in
    guard let strongSelf = self else { return }
            
    // Handle selection by responding to interaction state.
    if interaction.state == .selecting {
        strongSelf.selectItemsInRect(interaction.selectionRect)
    } 
    else if interaction.state == .ended {
        strongSelf.finalizeSelection()
    }
}

view.addInteraction(selectionInteraction)
Customizing a predefined pointer accessory position swift · at 33:01 ↗
var position = UIPointerAccessory.Position.topRight
position.offset = 40.0
Creating a custom pointer accessory position swift · at 33:14 ↗
let position = UIPointerAccessory.Position(offset: 23.0, angle: .pi * 1.25)
Pointer Accessories swift · at 33:27 ↗
// Attach two arrow accessories to a lift pointer effect.

func pointerInteraction(_ interaction: UIPointerInteraction, styleFor region: UIPointerRegion) -> UIPointerStyle?
{
    let preview = UITargetedPreview(view: self)
    let style = UIPointerStyle(effect: .lift(preview))

    if #available(iOS 15.0, *) {
        style.accessories = [
            .arrow(.left),
            .arrow(.right)
        ]
    }

    return style
}

Resources