Streaming is available in most browsers,
and in the Developer app.
-
Build with iOS pickers, menus and actions
Build iPhone and iPad apps with fluid interfaces and easily-accessible contextual information. We'll show you how to integrate the latest UIKit controls into your app to best take advantage of menus, date pickers, page controls, and segmented controllers. Learn how to adopt Menus throughly your user interface, and explore how UIAction can help unify your event handling. Once you've learned about these new controls, watch “Design with iOS pickers, menus and actions” to discover how to design great interfaces with these tools and APIs.
Resources
Related Videos
WWDC20
-
Download
Hello and welcome to WWDC.
Hello and welcome. I'm Eric Dudiak, an engineer on the UIKit team. And I'll be joined by David Duncan. We're going to be talking about some great new features of UIKit with a focus on standard controls and menus. So, let's get started. Here's a quick overview of what we're going to be covering in this session. We'll take a brief look at some of the recent changes in control appearances. After that, we'll go over the new color picker and how to use it.
Then we'll take a look at the updated date picker. We'll also look at how menus have changed and added more options. Finally, we'll go over improvements around UIActions that can significantly simplify your code.
So let's get started with the appearance updates to some common controls, and these apply across iOS and iPadOS, in both UIKit and their SwiftUI counterparts.
UISlider and UIProgressView have both received minor updates to make them more consistent across all of the platforms.
First, you'll notice the thickness of the track has been increased, bringing the appearance more in line with macOS. Along with the appearance changes, UISlider now adopts the macOS behavior of being able to tap or click on the track to adjust the value.
Here we see the matching visual change on UIProgressView.
When using these and other UIKit controls in an optimized Catalyst app, they will further adopt the macOS appearance. This further adoption will limit some of the customization APIs of these controls, and they will have no effect. For more information about the considerations when making an optimized Catalyst app, check out the "Optimize the Interface of your Mac Catalyst App" session.
The next control to receive an appearance update is UIActivityIndicatorView. The new design features fewer petals and is consistent across all sizes, along with some timing adjustments on the animation. Now, it is important to always use the built-in UIActivityIndicatorView when showing indeterminate progress. This ensures consistency. Now, to ensure it works in all possible scenarios, use the modern styles that adjust for Dark Mode and optionally allow setting a custom color via the API.
Similar appearance changes have also been made in the pull-to-refresh control.
And here we see the optimized Mac Catalyst version of these and a few other standard controls. Notice that they match the appearance of standard Mac controls.
UIPickerView has also received an update in its appearance. It should be noted, however, that menus may be an appropriate replacement in many contexts. We'll discuss them a little bit later in this session. On Catalyst apps on macOS, menus are almost always the best choice when you're presenting a selection from a number of choices.
The last control we'll go over is UIPageControl. Now, it has received both appearance changes and some functional changes. The new appearance allows the page control to support an unlimited number of pages within a fixed size.
Now when the number of pages exceed the space available, the control allows for the scrubbing and scrolling of those pages, making the control more ideal for scenarios where user customization may dictate the number of pages, such as on the Home Screen. Additionally, the page control features new API to customize its appearance.
The indicators themselves can now be set to custom images either for individual pages or all of the pages. This can be useful if a certain page has a special function, such as the first page in the Weather app shown here, which shows the current location icon for the weather.
The presentation and interaction of the control can also be customized, such as setting the control to display in a more prominent mode when it is the main control of an interface. So, let's take a look at how we use these customizations. Here we go ahead and create our pageControl.
We can then set the backgroundStyle to prominent, so that it'll stand out better in the context of our app. This will make the background area always show, instead of just when we're interacting with it.
We can also set the default image for all the indicators if we don't want the normal circles. In this case, setting the bookmark image for a case where we are browsing pages of bookmarks.
We can also further customize individual pages. Here we're doing that with the heart image on the first page to indicate that it is for favorites. All of the other API surface of the pageControl remains completely unchanged.
Next up, let's take a look at the new color picker in iOS 14.
For those already familiar with the macOS color panel, the new color picker in iPadOS and iOS 14 will feel very similar. The color picker is a view controller that can be easily presented as a sheet or popover from your application. In addition to typical delegate callbacks, it has a color property that can be set and read to configure it. Once presented, it allows selecting a color through a number of different methods, such as picking from a grid...
selecting a color from the entire spectrum gradient or manually specifying an exact color with red, green and blue components. Or even a hexadecimal code. Frequently used colors can also be favorited and reused across applications. So Pro Apps can allow users to easily select something important, like a brand color, in one context, and use it in many. Finally, just like the macOS panel, the color picker supports grabbing colors from anywhere on the screen using an eyedropper tool.
This is especially powerful on iPadOS when running multiple apps. The color can be easily selected anywhere on the screen, matched and then reused.
On macOS, the picker uses the standard color picker panel, allowing the same familiar functionality.
So, let's take a look at setting up and using the color picker. In this case, we have a ColorPickerViewController property on our existing viewController.
When the color button is pressed, we go ahead and set the color on the viewController to the current color being used and then present the ColorPickerViewController.
If the user finishes selecting a color, we go ahead and set it in the app.
If the user canceled selecting a color, we can safely ignore the color. At no point do we have to worry about how the color was selected or supporting the eyedropper tool. All of that is handled automatically by the color picker.
The date picker in iOS and iPadOS is not a new control, but we have made some really big improvements to the versatility and user experience of it.
New in iPadOS and iOS 14, the compact style is available and supported. Just like the macOS version introduced earlier, the compact style in iOS shows the time and date as fields that can be tapped to bring up a modal selection.
This is especially useful when space is limited in the UI, such as in a table view or form with several fields. When selecting from the date side, a modal calendar is displayed allowing for the easy selection of any given day.
This also allows for far more rapid selection of distant dates than the traditional wheel styles allowed and is better optimized for pointer interaction on an iPad. When tapping on the time, the keypad is used for selection of the time.
As mentioned earlier, the compact style is supported on macOS Catalina and later in the iOS 13.4 SDK and newer. This can help make your optimized or unoptimized Catalyst app better support showing a date picker on a Mac. When using it, the date picker control is sized similarly to a UILabel. Clicking on components of the date bring up a modal calendar presentation for selecting the date. The date and time can also be set by typing in values to the field. Additionally, if only the time or only the date are required for a given context, the app can limit the picker to just that part of the field. Additionally, iOS 14 introduces the inline style for the date picker. This is particularly useful in circumstances where selecting a date is the primary function of a UI and the addition of the modal step is simply unnecessary. This can be useful on iPad apps migrating from the wheel style where more screen space is readily available.
The presentation itself matches that of the modal one presented from the compact style, just filling the contents of the control rather than being presented from it.
The great part about all three new styles is that all the API of UIDatePicker remains exactly the same. So the new styles can be easily adopted or even adjusted, depending on the context, with only the layout of the app being affected. So now let's take a look at using these new styles. Here we see creating a date picker and setting the initial date has not changed at all on iOS 14. We can also set minimum and maximum dates for the selection. All we have to do to use one of the new styles is just set a preferred one. And here we'll set that to the compact style.
We can also customize the locale and calendar of the date picker if necessary. And the new styles will show the appropriate content. In this case, we'll show the Japanese calendar. And we can see how the era is now displayed where the year was previously.
We can also limit the selection to just the date if the time isn't relevant to this context.
The date picker is still just a UIControl. So it's easy to be notified when the value has changed and then read in the new date. The new date picker styles provide an improved experience when selecting dates in an application without the application having to handle many of the inherent complexities of different calendars or interactions across platforms.
Now I'd like to hand it over to my colleague, David Duncan. Thanks, Eric. I'm David Duncan, and I'm going to talk about enhancements to menus and UIAction. Let's start with how iOS 14 brings quick, lightweight interactions with menus to more parts of your UI. Any app in iOS 14 can easily add menus to UIButtons and UIBarButtonItems.
This example shows how Safari uses menus to put more power at your fingertips. Let's dive into this interaction.
Tapping on the tab switch button performs the default action, showing the Safari tab switcher.
On iOS 14, long pressing on this button presents a menu with more options. Immediately, you can slide your finger to select an item and lift to activate.
Let's see how to add a menu like this to your app.
UIButton and UIBarButtonItem directly support menus, and adding one couldn't be easier. Just assign a menu to the menu property of either class, and UIKit will take care of displaying that menu on long press.
But in some cases, you don't want to wait. Let's take a look at that interaction.
Reminders uses a More button to group several actions together.
Unlike our Safari example, the menu presents immediately when you touch the button. Just like before, the user can then slide their finger and quickly select an action in one smooth gesture. How you select this interaction differs between UIButton and UIBarButtonItem. For UIButton, setting showsMenuAsPrimaryAction to true causes the button to present its menu immediately on touch down.
For UIBarButtonItem, providing a menu but not setting a primary action indicates the menu should present on touch down.
On iOS 14, you'll also see menus automatically provided by the navigation Bar Back button.
This menu creates a standard accelerator for jumping back in the nav stack in any application.
Menu titles are automatically chosen considering customized back buttons where appropriate.
If you use a custom title view but don't set a title of a navigation item, consider setting backButtonTitle to ensure a good experience.
The new menu support you've seen so far is provided by UIControl.
You've already seen showsMenuAsPrimaryAction which determines if the menu triggers on long press or touch down.
UIControl provides access to its contextMenuInteraction and a property to enable that interaction. To support custom menu-based UIs, you can subclass UIControl and override its implementation of ContextMenuInteractionDelegate.
To take action when the menu gesture has been recognized, you can register for the menuActionTriggered control event. Now let's take a look at powerful new features in menus to complement the new places where you can use them.
UIDeferredMenuElement adds the ability to asynchronously provide menu items.
As our example shows, UIKit presents a standard loading UI while waiting for the final menu items to be provided.
Once provided, these items are cached should the menu be displayed again. UIDeferredMenuElement is also useful for generating complex menus, as the items won't be requested until they need to be displayed. Next, let's focus on two new features of UIContextMenuInteraction.
UpdateVisibleMenu allows you to update the menu currently presented to the user.
You receive a copy of that menu, and you return a menu to present in its place.
To make this API super easy to use, UIMenu has changed behavior to no longer force its children to be immutable, allowing you to update and return the menu passed to your block instead of creating a new one. UIContextMenuInteraction now provides a query for the interaction's appearance.
This property may return rich when displaying a preview, compact for menu-only interactions or none.
While controls always use the compact appearance, your own interactions may display rich or compact based on how they're triggered. That's some of the new capabilities in iOS 14 to bring menus to more places. Next, let's see how improvements to UIAction can make it easier than ever to act on user input. In iOS 13, UIKit introduced UIAction to make sharing event handling code easier. For a refresher, see "Modernize your UI for iOS 13" from WWDC 2019. iOS 14 expands where and how you can use UIAction in your app.
UIBarButtonItem adds new initializers to create items with actions and menus. They make it easy to create BarButtonItems that react to taps, present menus or both. And nearly every parameter is optional, so you only need to specify those that you need. Let's take a look.
Here we configure the toolbar items of our viewController using new API in iOS 14.
First, we create a system item that triggers an action when tapped and a menu when long pressed.
Next, we add a fixedSpace, using the new fixedSpacewidth API.
Then we add a custom item, configured to display an image and present a menu on touch down.
Now, a flexibleSpace, also using new API.
And finally, an item that will use the primaryAction's title or image and trigger the handler on tap. Like BarButtonItems, all controls can be constructed with a UIAction, but two controls, UIButton and UISegmentedControl, have additional behavior.
UIButton adds a new initializer accepting the button type alongside a UIAction.
The type defaults to system, and the primary action's title and image are used to configure the button when appropriate.
Like other controls created with a primaryAction, that action is registered to handle the primaryActionTriggered control event causing the action handler to be called when the button is tapped.
Before we look at Segmented Control's support for UIAction, let's see how you might configure one prior to iOS 14.
First, create the control with an array of strings. Next, add a handler to call a method, located elsewhere in your code. That method then has to switch over the selected segment assuming a mapping that may change. And when the mapping does change, the compiler can't help you catch the problem. iOS 14 makes this much easier.
With UIAction, creating segmented controls looks like this.
Create the control with an array of UIActions, each defining the title or image for one segment.
But unlike before, we don't need to add an event handler. UIKit automatically calls the action's handler when that segment is selected and only that segment.
No more need for a switch statement. No more default case to try to catch problems. The handler is right there next to control creation. And the compiler can help us keep configuration and response to user input in sync.
Here we use an enum to generate our actions. Which means we can just add a new enum case, and our segmented control's behavior updates automatically.
And you can still combine this with control event handling for the best of both worlds.
UISegmentedControl's adoption of UIAction starts with a new initializer and adds new methods for adding, removing, updating and finding segments.
As we've already seen, segments associated with a UIAction will have that action's handler called only when that segment is selected.
iOS 14 has a lot to offer to enhance your application for iPhones, iPads and Mac Catalyst. Refresh your UI by taking advantage of new appearances on stock controls, new customizability on UIPageControl and new styles for UIDatePicker.
Adopt ColorPickerController for all your color picking needs, making it easy for your users to select any color they can see. Make your application easier and faster to use with menus, where a quick slide of the finger can begin any task.
Support rapid navigation through your app by making sure back button menus are clear and correct.
And take action on UIAction to simplify and share code to handle user input. I can't wait to see how you enhance your applications. And thank you for watching.
-
-
4:34 - UIPageControl example
let pageControl = UIPageControl() pageControl.numberOfPages = 5 pageControl.backgroundStyle = .prominent pageControl.preferredIndicatorImage = UIImage(systemName: "bookmark.fill") pageControl.setIndicatorImage( UIImage(systemName: "heart.fill"), forPage: 0)
-
6:56 - UIColorPickerViewController example
var color = UIColor.blue var colorPicker = UIColorPickerViewController() func pickColor() { colorPicker.supportsAlpha = true colorPicker.selectedColor = color self.present(colorPicker, animated: true, completion: nil) } func colorPickerViewControllerDidSelectColor(_ viewController: UIColorPickerViewController) { color = viewController.selectedColor } func colorPickerViewControllerDidFinish(_ viewController: UIColorPickerViewController) { // Do nothing }
-
10:04 - UIDatePicker example
let datePicker = UIDatePicker() datePicker.date = Date(timeIntervalSinceReferenceDate: timeInterval) datePicker.preferredDatePickerStyle = .compact datePicker.calendar = Calendar(identifier: .japanese) datePicker.datePickerMode = .date datePicker.addTarget(self, action: #selector(dateSet), for: .valueChanged)
-
14:20 - UIDeferredMenuElement example
button.menu = UIMenu(title: "", children: [ UIMenu(title: "", options: .displayInline, children: (1...2).map { UIAction(title: "Static Item \($0)") { action in }}), UIDeferredMenuElement({ completion in DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) { completion([UIMenu(title: "", options: .displayInline, children: (1...2).map { UIAction(title: "Dynamic Item \($0)") { action in }})]) } }), ])
-
14:50 - updateVisibleMenu example
self.contextMenuInteraction.updateVisibleMenu { currentMenu -> UIMenu in currentMenu.children.forEach { element in guard let action = element as? UIAction else { return } action.state = Bool.random() ? .off : .on action.attributes = Bool.random() ? [.hidden] : [] } return currentMenu }
-
16:05 - UIBarButtonItem example
let saveAction = UIAction(title: "") { action in } let saveMenu = UIMenu(title: "", children: [ UIAction(title: "Copy", image: UIImage(systemName: "doc.on.doc")) { action in }, UIAction(title: "Rename", image: UIImage(systemName: "pencil")) { action in }, UIAction(title: "Duplicate", image: UIImage(systemName: "plus.square.on.square")) { action in }, UIAction(title: "Move", image: UIImage(systemName: "folder")) { action in }, ]) let optionsImage = UIImage(systemName: "ellipsis.circle") let optionsMenu = UIMenu(title: "", children: [ UIAction(title: "Info", image: UIImage(systemName: "info.circle")) { action in }, UIAction(title: "Share", image: UIImage(systemName: "square.and.arrow.up")) { action in }, UIAction(title: "Collaborate", image: UIImage(systemName: "person.crop.circle.badge.plus")) { action in }, ]) let revertAction = UIAction(title: "Revert") { action in } self.toolbarItems = [ UIBarButtonItem(systemItem: .save, primaryAction: saveAction, menu: saveMenu), .fixedSpace(width:20.0), UIBarButtonItem(image: optionsImage, menu: optionsMenu), .flexibleSpace(), UIBarButtonItem(primaryAction: revertAction), ]
-
-
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.