-
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.
Ressources
- Adding hardware keyboard support to your app
- Enhancing your iPad app with pointer interactions
- Human Interface Guidelines: Pointing devices
- Adding menus and shortcuts to the menu bar and user interface
- UIKit
Vidéos connexes
WWDC21
- Focus on iPad keyboard navigation
- Qualities of a great Mac Catalyst app
- Qualities of great iPad and iPhone apps on Macs with M1
- What's new in UIKit
WWDC20
WWDC19
-
Rechercher dans cette vidéo…
-
-
4:56 - Build an "Open in New Window" action
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) }) -
5:43 - Use an alternate action with UIWindowScene.ActivationAction
// 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) } -
6:58 - Present a scene from a collection view with a gesture
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) } -
7:28 - Present a scene from a custom view with a gesture
// 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) -
8:53 - Customize scene transition preview
// 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 -
10:18 - Save scene state
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 } -
11:16 - Save scene state with interaction state
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 } -
12:13 - Restore scene state
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 } } } -
13:15 - Restore scene state asynchronously
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() } } -
17:15 - Modify the main menu
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... } -
17:37 - Add key commands to the main menu
// 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) -
18:19 - Add a custom menu category
// 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) -
22:38 - Customizing key command performability
override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool { if action == #selector(closeTab(_:)) { return !openTabs.isEmpty } else { return super.canPerformAction(action, withSender: sender) } } -
23:26 - Customizing key command appearance
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) } } -
28:47 - Supporting multi-selection using UIBandSelectionInteraction
// 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) -
33:01 - Customizing a predefined pointer accessory position
var position = UIPointerAccessory.Position.topRight position.offset = 40.0 -
33:14 - Creating a custom pointer accessory position
let position = UIPointerAccessory.Position(offset: 23.0, angle: .pi * 1.25) -
33:27 - Pointer Accessories
// 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 }
-