Streaming is available in most browsers,
and in the Developer app.
-
Meet desktop-class iPad
Learn how you can bring desktop-class features to your iPad app. Explore updates to UINavigationBar that bring more discoverability and customizability to your app's features. Find out how the latest updates to UIKit can help make it easier and faster for people to explore content in your app. Lastly, we'll share a few updates on how it's easier than ever to bring your iPad app to the desktop with Mac Catalyst.
Resources
- Building a desktop-class iPad app
- centerItemGroups
- searchSuggestions
- Supporting desktop-class features in your iPad app
- titleMenuProvider
- UIDocumentProperties
- UINavigationItem.ItemStyle
- UINavigationItemRenameDelegate
Related Videos
WWDC23
WWDC22
-
Download
David Duncan: Hi, I’m David Duncan, and in this video, I’ll be introducing you to desktop class iPad. iOS 16 brings advances to the tools used to design and build great apps, apps that bring more and better tools to the forefront and take advantage of all the hardware, both built in and attached. UIKit adds many tools to help you meet these goals for your apps. Updates to UINavigationBar allow you to take better advantage of screen real estate and build a great experience on all Apple platforms.
The new Find and Replace UI is a snap to enable on built-in views and easy to add to custom ones. The Edit menu has been overhauled, with a new interaction-based API that integrates with the menu system. And collection view improvements make it easier than ever to build interfaces that let your users select and act on their content.
For more information on Find and Replace and Edit Menu, watch "Adopt desktop class editing interactions." And to see how all these features work together, watch "Build a desktop class iPad app." In this video, I'll discuss changes to navigation that impact how you design your app for iOS 16.
First are new features that make it easy to build more discoverable interfaces. Then features that are especially powerful for document based apps. And, finally, updates to Search to help accelerate and polish the experience.
UINavigationBar is used for many different purposes on iOS, and iOS 16 acknowledges that by providing new optimized UI for many of these cases. UINavigationItem adds a style property, used to select from these styles: navigator, browser, and editor. I'll dive into each of these styles now.
The default style, navigator, behaves exactly as a traditional UINavigationBar.
The title is centered, there are leading and trailing bar button items, and a back button appears when there is more than 1 item on the stack. The browser style rearranges contents to be better optimized for interfaces where history matters as much as location, like in Files or Safari.
The title is moved to the leading position in this styling.
The editor style is optimized for when the primary function is document editing. Just like the browser style, the title is leading aligned. Editor UIs are often a destination, such as after selecting a document with a document picker, and so present a back button for easy access to that UI.
The browser and editor styles both free up a lot of space in the center of the bar.
iOS 16 takes advantage of this liberated space by allowing you to place additional controls in this region.
Center items are part of a suite of changes to take better advantage of screen real estate, and include support for UIBarButtonItemGroup, customization support, and overflow.
Overflow support is available in all modes, and allows the navigator style to indirectly support center items as well.
Individual controls continue to be specified as UIBarButtonItems, but now are organized as UIBarButtonItemGroups. This allows for denser presentation when space is at a premium. In this example, there are 5 items in the bar, consisting of 4 groups.
The first group contains a single bar button item, so this example uses a convenience method of UIBarButtonItem, creatingFixedGroup(), to create it.
If you need a fixed group with more than 1 item, you can use the UIBarButtonItemGroup method instead.
Fixed groups always appear first in the bar, and cannot be removed or moved by customization. The draw group contains a single item, so it also uses a convenience API, creatingMovableGroup (customizationIdentifier). Like fixed groups, movable groups cannot be removed, but can be moved.
Because of this, they require a customizationIdentifier so their position can be tracked and saved. If you need a group with more than one item, you can use the UIBarButtonItemGroup method instead.
The shapes group contains multiple items, and so uses the UIBarButtonItemGroup API to create the group.
This group should be movable within the bar, as well as removable, and so is created as an optional group.
This group also specifies a representativeItem, allowing UIKit to collapse the group to save space when necessary.
The representativeItem does not specify an action, further allowing UIKit to synthesize a menu allowing selection of the items in the group.
When the customization UI is invoked, UIKit automatically applies the rules you've specified based on how you've created your groups. While fixed and movable groups must stay in the bar, optional groups can be added or removed in any number.
UIKit will try collapsing groups to keep as much functionality as possible in the bar, but if space isn't available, extra items will be moved to overflow.
The overflow menu contains any items that are part of the customization but could not be fit into the bar, as well as the option to customize the bar.
While UIKit will synthesize default menu elements for each bar button item, you have the option to customize the menuRepresentation if you wish. Finally, this example enables customization and adds the centerItemGroups.
You enable customization by setting UINavigationItem.customizationIdentifier.
The customizationIdentifier defines a unique customization of the bar, so pick a string that won't conflict with other customizations within your app.
UIKit automatically saves and restores customizations based on this identifier.
Next, provide the centerItemGroups themselves. The first four groups I've already covered.
The format group is an optional group that isn't in the default customization, and so this code overrides the default value of the isInDefaultCustomization parameter to exclude it. You can still use centerItemGroups without setting UINavigationItem.customizationIdentifier, but customization will not be available. In Mac Catalyst, the UINavigationBar automatically translates its content to NSToolbar.
The leading, center, and trailing item groups are added in order, and the customization properties of the center item groups are respected when using NSToolbar customization.
All of the expected NSToolbar behaviors are available, as well as other properties such as the title & window proxy.
All of this occurs by default when you optimize for the Mac. Next, let’s focus in on interactions that are powerful, specifically when dealing with documents. UINavigationBar now supports adding a menu to the title view, giving a central location to add actions that operate on the content as a whole. Additionally, you can add support for the share sheet and drag & drop from this menu. First, I’ll focus on the menu items themselves. Once enabled, the default title menu offers 5 commands: duplicate, move, rename, export, and print. These items are filtered based on specific methods in your responder chain. UINavigationBar has specific support for renaming, and so it will also be included if you’ve implemented a renameDelegate.
To enable the title menu, set the titleMenuProvider, a closure that returns the final menu to be displayed.
The closure is passed an array of suggested elements. You can use these as is, filter them, or add your own. In our example, we're adding a single additional action to the menu. Finally, you return the composed UIMenu.
The title menu also allows sharing via the activity view controller and support for drag & drop.
To enable these features, you provide a UIDocumentProperties instance that describes your document.
UIDocumentProperties represents metadata about your document, including a preview. This example creates one with a URL, allowing UIKit to fetch the necessary metadata automatically.
To enable additional features, this example creates an NSItemProvider to represent the document.
Set a dragItemsProvider to enable drag & drop. This closure is past a UIDragSession, and returns an array of UIDragItems. This example returns a single item representing the document.
Setting a activityViewControllerProvider enables sharing. This closure configures and returns a UIActivityViewController.
Finally, assign the filled-out object to UINavigationItem.documentProperties, and when the title is tapped, UIKit presents the header alongside other titleMenu items.
On Mac Catalyst, the suggested items that would be passed to the titleMenuProvider already exist in the File menu. Any items that you would have added to the title menu will need to be made available by other means.
You can use the UIMenuBuilder API to add these items, or filter existing items as necessary.
If you specify document properties, UIKit will automatically use the URL provided to manage the macOS proxy icon.
If you set the representedURL for your windowScene manually, that will supersede UIKit's management.
UIKit provides two mechanisms to enable Rename. Inline Rename is provided by setting UINavigationItem.renameDelegate, and provides a dedicated UI for editing the title on all platforms.
When completed, the resulting name is passed to the delegate.
Alternatively you can take full control over the rename experience by implementing UIResponder.rename(_:) and providing whatever UI you prefer.
On iOS, the UINavigationBar provides the rename UI directly within the title view. On macOS, the rename UI is provided by the window's title when the navigation bar is hosted in an NSToolbar.
To implement inline rename, conform to the UINavigationItemRenameDelegate protocol and set the navigation item's renameDelegate. There is only one required method, navigationItem(_:didEndRenamingWith:), that is used to receive the title accepted by the user.
For file based apps, UIDocumentBrowserViewController now offers a renamed API.
Search is how many users find their most important data, and advances in iOS 16 make it easier to provide an excellent search experience. The first thing to note is that search now takes up less space by being in line in the navigation bar on iPadOS and the toolbar on macOS. On iPadOS, you can restore the old behavior with UINavigationItem .preferredSearchBarPlacement. Additionally, the search bar can collapse to a button to grant more space for other controls. When search is activated, search suggestions appear, and they can be updated alongside the updating search query, allowing you the opportunity to assist your users in their search. Next, I'll describe the code needed to setup search suggestions.
To manage search suggestions, conform to UISearchResultsUpdating and set your searchController's searchResultsUpdater. This allows you to update suggestions as the query changes and to act on a selected search suggestion.
When the query changes, updateSearchResults(for:) is called, allowing you to update search suggestions.
What suggestions to provide is up to you. Setting an empty array will clear the suggestions UI.
UIKit provides UISearchSuggestionItem to specify suggestion content.
To respond to the selection of a suggestion, implement updateSearchResults(for:selecting:). This method passes the selected search suggestion, so you may react to it appropriately. In this example I update the search by replacing the current query with the one specified by the search suggestion. UISearchTextField also has searchSuggestions, so if you prefer to use that class on its own, you can still implement search suggestions. But if you are using UISearchController, you should use its property instead.
In iOS 16, UIKit provides new API to help you bring more productivity to your users. Bring more discoverability to your advanced features with center items and the title menu.
Improve your document support by providing drag & drop and sharing directly from the navigation bar.
Make it easier and faster to search by providing search suggestions and get a great Mac experience right out of the box, with nearly zero effort. Thanks for watching this video. I can't wait to see how you enhance your apps to be desktop class!
-
-
4:27 - Creating a fixed UIBarButtonItemGroup from a single UIBarButtonItem
let insertGroup = UIBarButtonItem(title: "Insert", image: UIImage(systemName: "photo"), primaryAction: UIAction { _ in }).creatingFixedGroup()
-
4:55 - Convenient form
// Creating the 'Draw' group // Convenient form of // UIBarButtonItemGroup.movableGroup(customizationIdentifier:representativeItem:items:) let drawGroup = UIBarButtonItem(title: "Draw", …) .creatingMovableGroup(customizationIdentifier: "Draw")
-
5:30 - Creating an optional group with multiple UIBarButtonItems using UIBarButtonItemGroup
let shapeGroup = UIBarButtonItemGroup.optionalGroup( customizationIdentifier: "Shapes", representativeItem: UIBarButtonItem(title: "Shapes", image: UIImage(systemName: "square.on.circle")), items: [ UIBarButtonItem(title: "Square", image: UIImage(systemName: "square"), primaryAction: UIAction { _ in }), UIBarButtonItem(title: "Circle", image: UIImage(systemName: "circle"), primaryAction: UIAction { _ in }), UIBarButtonItem(title: "Rectangle", image: UIImage(systemName: "rectangle"), primaryAction: UIAction { _ in }), UIBarButtonItem(title: "Diamond", image: UIImage(systemName: "diamond"), primaryAction: UIAction { _ in }), ])
-
6:56 - Setting up customizable centerItemGroups on a UINavigationItem
navigationItem.customizationIdentifier = "com.jetpack.blueprints.maineditor" navigationItem.centerItemGroups = [ // groups in the default customization UIBarButtonItem(title: "Insert", image: UIImage(systemName: "photo"), primaryAction: UIAction { _ in }).creatingFixedGroup(), UIBarButtonItem(title: "Draw", image: UIImage(systemName: "scribble"), primaryAction: UIAction { _ in }).creatingMovableGroup(customizationIdentifier: "Draw"), .optionalGroup(customizationIdentifier: "Shapes", representativeItem: UIBarButtonItem(title: "Shapes", image: UIImage(systemName: "square.on.circle")), items: [ UIBarButtonItem(title: "Square", image: UIImage(systemName: "square"), primaryAction: UIAction { _ in }), UIBarButtonItem(title: "Circle", image: UIImage(systemName: "circle"), primaryAction: UIAction { _ in }), UIBarButtonItem(title: "Rectangle", image: UIImage(systemName: "rectangle"), primaryAction: UIAction { _ in }), UIBarButtonItem(title: "Diamond", image: UIImage(systemName: "diamond"), primaryAction: UIAction { _ in }), ]), .optionalGroup(customizationIdentifier: "Text", items: [ UIBarButtonItem(title: "Label", image: UIImage(systemName: "character.textbox"), primaryAction: UIAction { _ in }), UIBarButtonItem(title: "Text", image: UIImage(systemName: "text.bubble"), primaryAction: UIAction { _ in }), ]), // additional group not in the default customization .optionalGroup(customizationIdentifier: "Format", isInDefaultCustomization: false, representativeItem: UIBarButtonItem(title: "BIU", image: UIImage(systemName: "bold.italic.underline")), items:[ UIBarButtonItem(title: "Bold", image: UIImage(systemName: "bold"), primaryAction: UIAction { _ in }), UIBarButtonItem(title: "Italic", image: UIImage(systemName: "italic"), primaryAction: UIAction { _ in }), UIBarButtonItem(title: "Underline", image: UIImage(systemName: "underline"), primaryAction: UIAction { _ in }), ]) ]
-
9:30 - Adding a "Comments" item to the default title menu
navigationItem.titleMenuProvider = { suggestedActions in var children = suggestedActions children += [ UIAction(title: "Comments", image: UIImage(systemName: "text.bubble")) { _ in } ] return UIMenu(children: children) }
-
10:15 - Supporting Drag & Drop and Sharing from the title menu
let url = <#T##URL#> let documentProperties = UIDocumentProperties(url: url) if let itemProvider = NSItemProvider(contentsOf: url) { documentProperties.dragItemsProvider = { _ in [UIDragItem(itemProvider: itemProvider)] } documentProperties.activityViewControllerProvider = { UIActivityViewController(activityItems: [itemProvider], applicationActivities: nil) } } navigationItem.documentProperties = documentProperties
-
12:45 - Implementing inline rename
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() navigationItem.renameDelegate = self } } extension ViewController: UINavigationItemRenameDelegate { func navigationItem(_ navigationItem: UINavigationItem, didEndRenamingWith title: String) { // Try renaming our document, the completion handler will have the updated URL or return an error. documentBrowserViewController.renameDocument(at: <#T##URL#>, proposedName: title, completionHandler: <#T##(URL?, Error?) -> Void#>) } }
-
14:05 - Implementing Search Suggestions
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() searchController.searchResultsUpdater = self } } extension ViewController: UISearchResultsUpdating { func fetchQuerySuggestions(for searchController: UISearchController) -> [(String, UIImage?)] { let queryText = searchController.searchBar.text // Here you would decide how to transform the queryText into search results. This example just returns something fixed. return [("Sample Suggestion", UIImage(systemName: "rectangle.and.text.magnifyingglass"))] } func updateSearch(_ searchController: UISearchController, query: String) { // This method is used to update the search UI from our query text change // You should also update internal state related to when the query changes, as you might for when the user changes the query by typing. searchController.searchBar.text = query } func updateSearchResults(for searchController: UISearchController) { let querySuggestions = self.fetchQuerySuggestions(for: searchController) searchController.searchSuggestions = querySuggestions.map { name, icon in UISearchSuggestionItem(localizedSuggestion: name, localizedDescription: nil, iconImage: icon) } } func updateSearchResults(for searchController: UISearchController, selecting searchSuggestion: UISearchSuggestion) { if let suggestion = searchSuggestion.localizedSuggestion { updateSearch(searchController, query: suggestion) } } }
-
-
Looking for something specific? Enter a topic above and jump straight to the good stuff.
An error occurred when submitting your query. Please check your Internet connection and try again.