Modernizing Your UI for iOS 13

Session 224 WWDC 2019

iOS 13 combines powerful new multitasking and productivity technologies with a refreshed look and feel for all applications. Familiarize yourself with new requirements to keep your app ready for upcoming versions of iOS. Discover improvements to search fields and a totally new way to present UI. Learn how to adopt new gestures around selection, access peek and pop on all hardware, and how to renew your app's look for iOS 13.

[ Music ]

[ Applause ]

Hello everybody.

This is modernizing your UI for iOS 13.

I'm David Duncan.

I'll be joined on stage shortly by my colleagues Russell, Kyle, James, and Mohammed.

In this section, we're going to talk about six things.

Preparing your applications for Flexible UI.

Improvements that we've made the appearance of Bars and Presentations, new Features available to your applications for searching, and new Gestures that you can adopt to make your apps more productive and to take care of cases that you haven't been able to do before.

And finally, we're going to go over those new contextual menu APIs that were hinted at earlier this week.

And so with this, let's talk about Flexible UI.

So, the first step to being flexible is what your users first see when they launch your application.

Storyboards have been the preferred way to specify your Launch UI since iOS 8.

But there's also been another alternative that we've been supporting since the beginning of iOS, and that's Launch Images.

Launch Images require you to specify an image for every screen size you support and revise whenever we introduce new screen sizes.

And that's not very flexible.

And so, this spring, April 2020, applications that linked iOS 13 SDK must provide a Launch Storyboard in order to be accepted at the app store.

You will no longer be able to submit with only Launch images.

So, if you haven't adopted Launch Storyboards yet, now is a fantastic time to do so, and that leads on to the next change that you may have also already heard of, which is that in the past if we introduce new hardware with a new screen size, your apps were letterboxed.

Well, we're not going to be doing that anymore either.

So, if you application is built against the iOS 13 SDK, then it will always be displayed at the native full-screen resolution of the screen.

So we expect that any application built against iOS13 will use the appropriate APIs to ensure correct layout at any size.

Now, for those of you building iPad applications, this also applies to split screen multitasking.

So, we are going to expect that most applications, unless you need to provide a truly immersive experience, are going to support split screen multitasking, so that you can have your app in any size next to any other application that might be running that the user has chosen.

Now, if you're not certain if you can support all these resizing modes and everything else, another thing you might want to try, especially if you have an iPad app, is just building for the Mac, with new supports that we're adding in macOS Catalina.

And you can just do that at your desk to make sure that you resize nicely for any size.

So, to sum up all that before, adopt Launch Storyboards.

They're going to be the way to get that Launch UI when the user first launches your application.

Make sure your application properly lays out to support any size, whether it is the smallest iPhone to the largest iPad.

And finally, make sure that your iPad applications also support Split Screen Multitasking.

Because all of this is going to be required by April 2020.

And so with that, let's transition to Bars.

So, if you look at your phone after installing the beta, you'll see this is how nav bars now appears.

When you scroll all the way up to the top, we drop out the background, and when we scroll something underneath, we bring it back in smoothly and transparently.

Similarly, on iPad, if you have split view controller, we do this independently for both sides regardless of if the details support large titles or not, and they independently respond.

So, one background comes in or the other, depending on the scroll position.

Now, you might be asking, how do I adopt this?

How do I make sure this works cleanly in my application?

Well, the adoption story is easy.

You link against iOS 13, you get this for free.

But how to make that work beautifully in your application might require a little work, and that's where we have new appearance customization APIs to help you do this.

So, let's take a look at what it might look like to customize a navigation bar in the new appearance API.

So, first thing we're going to do is create the object that actually represents the appearance, UINavigationBarAppearance .

This is a subclass of UIBarAppearance, and it encapsulates all the customization options for UINavigationBar.

In this case, we're going to actually make the navigation bar use an opaque color, and so we're going to use the default values provided by configureWithOpaqueBackground.

That will make your navigation bar's background conform to light and dark mode with an appropriate opaque color.

Now, in this particular case, we also wanted to change the label color.

And so, we can set titleTextAttributes and largeTitleTextAttributes, and we'll just give them a custom color that we'll use, another dynamic color, just so that we work great in light and dark modes.

And finally, we will attach the navigation bar's standard appearance.

So, what is that standardAppearance?

Well, let's take a look.

The navigation bar, when it's in its size without a large title is in the standard size, and that's what standardAppearance represents.

In addition, if you don't specify any other appearance configuration, we'll use that as the basis to form defaults for the other two, which I'll talk about in a moment.

So, if you use a smaller iPhone in Landscape, you get the compactAppearance, and that's what that represents, quantities for that.

And we saw earlier that in iOS 13 the navigation bar dropped its background to transparent when you pull down at the top of a scroll view.

And so that is the scrollEdgeAppearance.

Whenever a navigation bar is associated with a scroll view, which is pretty common in your apps, if you're at the top of that scroll view, then we'll use the scrollEdgeAppearance instead of the standard appearance.

The default is to use a transparent background, and that's why you get the seamless appearance that we've got new in iOS 13.

In addition, you can also customize the appearance of your bar button items, and so the buttonAppearance for plain items and the doneButtonAppearance for done items.

Now, we could have stopped there.

You got your navigation bar.

It's customized.

It's great.

But we decided to also bring this to Toolbar and TabBar so that you can customize all your bars in a very similar way with each of those classes using their own subclass of UIBarAppearance to do their own customized work.

Now, ToolbarAppearance is actually just a subset of properties relative to navigationBarAppearance, so it's not too interesting to continue further with, but TabBarAppearance is a little bit different, and that's because TabBar is also a little different.

So, with a TabBar, you have additional customization options for these three layout appearance, the stackedLayoutAppearance, the inlineAppearance, which you'll see on iPads, and the compactInlineAppearance, which you'll see on smaller phones.

And so, you can put all that together and get new appearance customization for all of your bars.

But there's one more thing.

If you look at the new reminders app, when you navigate into a list, this title color changes to match the list.

The way they do this is they use per navigation item appearance customization, which allows you to specify an appearance based on the navigation item that you pushed onto the navigationBar.

Navigation items are also associated with navigation, or with view controllers, and so pushing onto a navigation controller will naturally use that.

So what does that look like?

Well, the navigation item has the same properties that the navigationBar has.

So a common thing to do is to just take the standard appearance from a navigationBar, make a copy of it so that we are disassociated from that appearance, and make whatever changes you want.

If you want this to be transparent, there's a configure transparency that you can use.

You can change bar button item appearances, etc., etc. And then you assign it to the navigation item.

And once you've done that, whenever the view controller and navigation item is current, we will use that appearance instead of the navigationBar's base appearance to make sure that you can customize your navigationBar for whatever view controller gets pushed.

And so with that, I'm going to bring on Russell to talk about presentations.

[ Applause ]

Thanks David.

Hi everyone.

I'm Russell, and new in iOS 13 we have a standard design for presentations that I am so excited to tell you all about.

For example, if I'm here in the contacts app and I tap plus to add a new contact, we have a new presentation style that looks like this, rather than the full screen presentation that we had before.

You can see that the root view controller's view is not as scaled down and not removed from the view hierarchy.

The layering of this design gives your users a sense of context about where they are in your applications, and the rounded top appearance serves as a signifier that these presentations can be interactively dismissed anywhere in the system [applause].


[ Applause ]

So what is this new presentation style?

These presentations are called Sheets, and Sheets aren't new at all.

These presentations are just a new design for the existing UIModalPresentationStyle pageSheet and its sibling, formSheet.

Previously, these styles would adapt to a full screen presentation in a compact width environment.

And now, they just stay Sheets with their own special layout and compact width different from their regular width layout.

So, let's look at a few examples of this in action.

In phones and portrait, you get the appearance that you just saw.

In phone in landscape, you get a full screen layout like before.

But iPads aren't left out.

We've rethought Sheets here as well.

Page Sheets float in a stack in the middle of the screen, and if multiple are presented on top of each other, they'll form a stack on top the review controller.

And specifically, this new size follows the readable width, so it's perfect for textual content.

And because the readable width changes with the user's currently selected dynamic type size, these page sheets can change size as well, like this or even like this.

Or they can take on a stacked appearance if their preferred width is wider than the available width.

So, what do you all need to do to get this new appearance in your app?

Well very little because you've done a lot of work to make this transition as easy as possible.

The default UIViewController ModalPresentationStyle has changed to a new style called Automatic.

Automatic is a dynamic presentation style, which is resolved at the time of presentation.

To illustrate its behavior, let's walk through a few examples together.

So, here I have a custom view controller, and it's going to present UIImagePickerController showing the user's photo library.

And note that there's no modal presentation style set.

This same code that worked on iOS 12 and presented the image picker full screen will now present as a sheet with no code change required on your part.

On the other hand, if I configure the UIImagePickerController to present in camera mode, this code will present the camera full screen in iOS 13, just like it did in iOS 12.

This is automatic at work, resolving to different styles depending on how a system provided view controller is configured so that you don't have to make any code change.

Now, what about presenting our own view controllers?

If I simply instantiate and present a custom subclass of UIViewController it will present as a Sheet.

In other words, by default, automatic resolves to pageSheet.

And this is great because it's the right style for most presentations.

Again, no code change required.

But, what about this last case?

If you have a custom view controller that should be presented full screen, such as your own custom camera or other immersive experience, you may find that when you build this code, your immersive experience has been turned into a Sheet.

But don't worry, all you have to do to fix it is explicitly set the modalPresentationStyle of this view controller to full screen.

In general, it's good to leave properties at their default values and only specify an explicit value when you're intentionally deviating from the default behavior.

And what about Popovers?

Well, Popovers always adapt to sheets now.

So, if you want a Popover in regular width and a Sheet in compact width, all you have to do is set your modal presentation style to be a Popover, and you're done.

You'll get exactly that behavior.

Now, what do you all have to do to support Pull to Dismiss?

In general, nothing.

If you present something as a Sheet, the ability to pull it down comes for free.

We will place a gesture recognizer on your entire presented view, so pulling down in any noninteractive area will trigger a pulldown on a Sheet.

And, if the user pulls down past the top in any contained scrollView, that will also pull the sheet down as well completely for free.

However, there are times when pulling down a sheet maybe undesirable.

For example, if we present this same Sheet and enter some data, the Sheet should not be allowed to be pulled down, because it's unclear if the user intends to discard or save their changes, and if the user does attempt to pull down, the Sheet should rubber band, and an action sheet should be presented, showing what actions the user can take to escape this context.

So, to create this experience, we have two new APIs.

The first is modal and presentation, is a property on UIViewController, and when you set this to true on your presented View Controller, it will put the Sheet in a modal state where it cannot be dismissed, and you'll get the rubber-banding effect, just like you saw.

This property supersedes the existing isModal and Popover property to prevent both Sheets and Popovers from being dismissed.

The second is a new UIAdaptivePresentationController DelegateMethod called presentationControllerDidAttempt ToDismiss.

So if you set the delegate on the presentation controller involved, UIKit will call this method when the user pulls down while in a modal state, at which point you can present the action sheet.

Here, you can see the relationship between these APIs.

DidAttemptToDismiss is only called if isModalInPpresentation is true and the user pulls and releases with the intent to dismiss, so with some force or velocity.

We call this the Modal Flow, and we have a sample project that shows exactly how to implement an example like this.

See the link in the description for how to download that.

But, we have more.

We also have a few more delegate callbacks.

PresentationController ShouldDismiss, which is useful for preventing dismiss from the delegate, WillDismiss, which is a great place to grab one of the view controller's transition coordinators, which you can use to set up alongside animations, interaction change notifications, and animation completion blocks and DidDismiss, which is great for cleaning up state because it's only called once if the user actually pulls the Sheet down and completes the transition.

These three callbacks supersede similarly named UI Popover presentationControllerDelegate Callbacks because these APIs are called for both Sheets and Popovers.

Now, keep in mind that if a user were to repeatedly tug on a Sheet without pulling it down, the delegate will receive, Should and WillDismiss multiple times before calling DidDismiss, if it receives DidDismiss at all.

Now, let's shift to Share Extensions for a moment.

When you build with the iOS 13 SDK, your Share Extensions, just like mail here, will become Sheets, and Pull to Dismiss will work here as well.

So, on the principle view controller of your extension, which is like the main view controller you specify in the Info P List, be sure to set isModalInPresentation once the user has started entering data, and if you make your principle view controller conform to UIAdaptivePresentation ControllerDelegate and implement DidAttemptToDismiss, we will call that method when the user pulls down in this case, but note that we will not call any of the other delegate methods.

And this behavior will be available in a future seed.

Moving right along, I have a few things for you to consider.

The first has to do with UIViewController Appearance Callbacks.

Specifically, the difference between the appearance callbacks that the presenting view controller receives during a full screen presentation as compared to a sheet presentation.

During a full screen presentation, the presenting view controller's view is removed from the view hierarchy, and as a result, it receives viewWillDisappear when the presentation transition begins and then viewDidDisappear when the presentation transition finishes.

And similarly, during the dismissal transition when its view moves back into the view hierarchy, it receives viewWill and DidAppear.

But, for Sheet Presentations, the presenting view controller's view is not removed from the view hierarchy, and so it's view, or the view controller does not receive any of these appearance callbacks.

So, if you have code in these callbacks that you need to be executed when a Sheet dismisses, you should look at the presentation controller Will and DidDismiss methods that we just talked about.

The second thing I want you all to consider is that we have inserted some private views in the view hierarchy in between UIWindow and its rootViewController's view.

This change should not affect you.

We just don't want you to be surprised when you see these views it the View Debugger.

The relationship between UIWindow and it Root View Controller's View is an implementation detail of UIKit, and because it is UIWindow's responsibility to manage and add this view to the view hierarchy.

In general, the structure of a view hierarchy not owned by your app is liable to change without notice in any release.

So, always ensure that you're writing your code in a way that doesn't make assumptions about private view hierarchies.

So, use Sheet presentations in your apps.

They're meant to be used for most UI with few exceptions, and implement the modal flow when pulling down requires the user's intent.

And remember, there's a sample project that illustrates exactly how to do that in more detail in the session's description.

Thanks everyone, and now I'm going to hand it off to Kyle to tell you more about Search.

[ Applause ]

Good job.

Thank you, Russell.

Hello everybody.

My name is Kyle.

I work on UIKit, and I'm going to tell you some new things in iOS 13 about Search.

Now this isn't new.

This is the existing interface for searching in the mail application, and it is provided by UIKit.

Specifically, the UISearchController subclass of UIViewController.

It's familiar to all of our users, and it's available in your applications too, and it consists of a number of parts.

The biggest piece is a UISearchBar, which you can see here at the top of the screen.

The search bar has a couple of components including a Cancel button and what we call the Scope Bar, which is that row of buttons underneath the search field.

Now, you may notice the updated visual appearance of the Scope Bar in iOS 13, but now we're allowing our application to hide elements of the Search Bar even if it's managed by a UISearchController.

So, if your application doesn't need a Scope Bar or a Cancel Button but wants to make them available perhaps depending on the UI state.

You can control that directly with new properties on UISearchController.

Also, we're exposing the SearchTextField as a public property on UISearchBar, which means that it's really easy to customize.

[ Applause ]

So to customize the appearance of your SearchController, set these properties to false on UISearchController and use the existing properties on UISearchBar to control the visibility of the Cancel Button and the Scope Bar.

Use the searchTextField Property to get a reference to the UISearchTextField instance and change all the properties on the Text Field that you'd like to change.

It's a subclass of UITextField and all of the styling properties are available there.

UISearchController's behavior extends to the appearance and dismissal of Search as well, so the standard behavior is that when you activate the Search Bar by tapping on its Search Field, it gets hoisted to the top of the screen, but the content that was visible on the screen before remains visible.

As you type, in this case the photos application displays results that match your partial search term, and this is implemented with a UIViewController that your application provides called the Search Results Controller.

Not every application behaves this way.

For example, the mail application, as soon as you tap on the Search Field provides a list of suggested searches.

This is still a UISearchController's Search Results Controller, and we're making this functionality available to you in iOS 13 as well.

[ Applause ]

Just set the showsSearchResultsController property on your Search Controller to true, and you can manage the automatic behavior with automatically shows search results controller property.

There's another big feature in Search.

In Mail and Photos and other applications on the system, these suggested searches result in what we call a token, which is a visual representation of a more complicated search query.

This functionality is available in your application on any instance of UISearchTextField via the new UISearchTokenAPI.

And these tokens support Copy and Paste and Drag and Drop.

It's the same implementation that we use for all of our applications.

[ Applause ]

For example, Photos uses it to provide saved or predicted searches for people, places, and objects.

Tap on any of the entries in the SearchResultsController, and Photos transforms even a partial input into a token.

As this is a subclass of UITextField, the tokens do interact with the text.

In fact, they're always prior to the text.

For example, if I pace the token into the search field, even though my insertion point is at the end of the search field, the token appears at the beginning, and as you can see here, the token is now selected.

And I can extend the selection to include both the token and some text.

Creating tokens is pretty easy.

First, grab the selected text range to figure out what text you want to convert into a token.

Create a UISearchToken object, and call this method on the SearchTextField to replace the textual portion of that range with a token.

Your app is in complete control over the insertion and removal of tokens, so you can attach any meaning to them that you want.

Now, UITextField conforms to a protocol called UITextInput.

If you're doing programmatic selection, it's important to understand some aspects of UITextInput, namely ranges and positions.

In a normal text field, every character is assigned a UITextPosition, and these positions are relative to two well-known positions at the beginning and the end of what we call the document, which is the search field's content.

And you can use these positions to create UITextRanges.

For example, to programmatically select a portion of the field's content.

Because tokens are selectable, they too get UITextPositions.

But the length of the text of a search field is now different from the distance from the beginning of the document to the last character's position.

If this matters to you, for example, to programmatically select a portion of the text, UISearchTextField exports this new property, textualRange.

The beginning of the textualRange is the first nontoken character in the field, and its end is the end of the document.

These match up with character indexes in the text property.

So, to take advantage of all of this new functionality in your application, take a look at the new properties and customization options available on UISearchController.

Use UISearchTextField either inside the UISearchBar or anywhere else you can use a UITextField today, and customize it with all of the customization options that are available on UITextField.

Adopt UISearchToken to represent complicated queries in a concise editable fashion that supports Copy and Paste and Drag and Drop.

There's a lot of advanced techniques you can do with this, so we're going to have a sample project available soon at this session's website, that demonstrates how to use tokens effectively.

Now, I'm going to hand it over to James, who's going to talk about Gestures.

[ Applause ]

Thanks Kyle.

Hey everybody.

I'm James McGaran, and I'm here to show you some new Gestures that we've added in iOS 13 themed around selection, organization, and some common editing shortcuts.

So this is everything I'm going to be talking about today.

The best news you're going to hear all day is that a lot of this behavior is actually automatic, stuff that you're going to get for free, but I'm here to tell you about what's different and what you need to do in order to take full advantage of these new features inside of your application.

So, let's start with some new features regarding text selection gestures in custom text views.

So, as you know, there's a lot of really great existing ways to select text on iOS using Gestures.

You can tap and hold to start highlighting right away, that's a new thing in iOS 13.

You can long press to get the selection loop and move the insertion point around, and you can triple tap on a paragraph to select the entire paragraph all at once.

So, these Gestures work great in native text widgets like UITextView and UITextField.

You already get those.

But, there might be some cases where you need to go beyond what TextView and TextField are capable of.

Some of you need to implement highly customized text drawing in your apps.

So, here's books as an example, where they use a completely customed text view to control the margins, character spacing, etc. So, currently, in order to get selection behavior like this in a custom text view, you had to manually add all of the system text selection Gestures to your app just to get parity with the native text widgets.

Many of you also had to implement you own UI for selection, like the selection rectangles, the blue selection rectangles or the selection handles just to offer this behavior.

So, I'm pleased to announce in iOS 13 this can be accomplished much easier with a new type of UIInteraction called UITextInteraction.

So, in case you're not familiar with UIInteractions, you can think of them as a way to encapsulate sort of a set of behaviors and Gestures related to UIKit widgets.

Think of Drag and Drop interactions, similar to that.

So it's only three lines of code to add all of the system test selection Gestures to your app, the ones that your users are already familiar with.

We provide a set of Gestures for editable and noneditable text interactions, and you can use the UITextInputProtocol to get more fine grain control over the selection UI, like the rectangles and the handles that I was mentioning before.

So here's what you need to do to get started with UITextInteraction in your Custom Text View.

So, as I said, it's only three lines of code to do this, minus the comments.

So you create the interaction using UITextInteraction 4, and let us know if you want the set of Gestures for editable or noneditable text fields.

Then assign the text input property to your view that implements the UITextInputProtocol.

We keep this separate in case you want Gestures to work on a container view, like a scroll view, but you want all of the text selection behavior to get handled by some contained view, like the one that's actually drawing the text.

And finally, just add the interaction to your view with Add Interaction.

Well, take a trip [ Applause ]

Take a trip with me over to this side of the stage, and we'll talk about multiple selection Gestures in TableViews and CollectionViews.

So we've long supported Multiple Selection Mode in UICollectionView and UITableView.

Apps like Notes here have a Select button on the top right corner where users can tap it to enter multiple selection mode and then individually select each individual note one by one to group them together and then organize them with Drag and Drop or one of those action buttons at the bottom there.

Well, I'm pleased to announce in iOS 13 we're introducing a totally new way of quickly selecting a contiguous batch of items in collection views and table views.

So, let me show you what that looks like here on this giant iPad.

So now, users will be able to instantly jump into multiple selection mode by simply placing two fingers down anywhere on a table or collection view and pan across to start selecting right away.

Thanks, that's really awesome.

So, here you can see taking two fingers and sliding down the list view in files will instantly put it into edit mode and start selecting cells.

Even cooler, the same works for a grid of items in a UICollectionView flow layout, so your users will be able to pan two fingers across a grid of items and select a bunch of things all at once.

This is absolutely a dream come true for Drag and Drop.

Not to mention, if your users have a hardware keyboard attached, they can also hold the shift or command key to tap on other items, and it behaves just like it does on macOS.


[ Applause ]

And now especially that your iPad apps can run on the Mac, your table views can feel right at home.

So, these awesome new behaviors are currently opt in, and the reason why is because your app might need to adapt around the fact that your user intends to enter multiple selection mode.

For instance, you might want to change the select or edit button to say cancel or done instead.

You might want to show action buttons at the bottom of the screen for stuff to do with the things that your user selected, or you might to disable some UI like the add not button in this case.

So, to account for this, we're adding just a couple of new APIs to help you adapt to these new behaviors.

It's really easy.

There's only two delegate methods to implement, and there's roughly equivalent versions on both Table View and Collection View.

So here they are.

The first one to implement is just Should Begin Multiple Selection Interaction at Index Path.

Or you can return true if you would like to allow the gesture to begin.

And we give you the index path here in case you'd like to prevent the selection from beginning on a part of your table view that's not selectable, in which case you can just return false.

And the second method to implement is did begin multiple selection interaction at index path, where you can adapt your surrounding UI to account for the fact that the TableView was automatically put into Multiple Selection Mode, just like we're talking about on the previous slide.

So moving on, the last thing I want to talk about is just briefly cover some enhancements that we've made to some common editing tasks using some new gestures.

So, in iOS 13, we're introducing a standard set of Gestures to make it easier to do some common editing tasks.

For example, using three fingers, users can now swipe back to undo and then swipe the other way to redo.

Pinching three fingers in will copy, and then pinching them out will paste.

So, we're bringing this standard set of productivity gestures to the system so that you don't have to worry about implementing UI for these common editing commands.

It's unified across the whole system so your customers will be able to discover right away within your app, and they'll be instantly familiar with it.

It's really great for drawing apps because you don't need to show any floating panels or Toolbars in order to offer this behavior.

And best of all, it's completely free if you're already using undo manager or NS undo manager.

So, my call to action here is for you to try these gestures in your app when you get a chance and make sure that they work really well.

But just in case, I can see some of you are nervous, so just in case they conflict with other three-finger gestures that you might have within your app, we're offering an escape hatch via some new API.

It's just a simple property that you override on UIResponder called EditingInteractionConfiguration, where you can return none if you'd like to opt out of these gestures.

So here's the takeaway, I hope, for today.

Finally offer system-wide standard text selection gestures in your customized text widgets.

Change the way your users organize information in your app using multiple selection gestures.

I really hope you adopt that one.

And finally, allow your users to stop shaking their iPad and control the undo stack with a standard set of gestures.

[ Applause ]

Yeah, it hurts.

So, thanks.

Now I'd like to hand it off to Mohammed to talk about menus.

[ Applause ]

All right.

Thanks James.

Hi everyone.

I'm Mohammed, and I'm excited to talk to you about some powerful new APIs we're introducing in iOS 13 that allow you to present fluid interactive menus with rich previews in your apps.

The first of these APIs is UIContextMenuInteraction.

UIContextMenuInteraction is a UIInteraction that lets you present menus with rich previews and complex hierarchies.

These hierarchies can have nested sub-menus and in-line sections.

We'll see what that means later on.

The menu presented by the center action is highly adaptive, meaning it'll do the right thing depending on the context in which it's presented.

So, here we see it on an iPhone in portrait.

We have the preview and the actions kind of vertically stacked above one another.

On an iPhone in landscape, we get the preview and the action side by side to better take advantage of the available space.

And on an iPad, it reconfigures itself depending on the available space and size of its preview and actions list.

And since this is a cross-platform API, if you're running your app on the Mac, it turns into a familiar macOS context menu.

The interaction provides a consistent experience by presenting the menu using the same gesture throughout iOS.

So users will come to expect that this gesture brings up a menu anywhere in the system.

The gesture will adapt to the device's capabilities.

So, on phones with 3D touch, we will use 3D touch to bring up the menu quickly and provide some nice feedback.

On phones without 3D touch capability, we'll use Haptic Touch, and we'll keep that nice feedback whenever the menu pops up.

On other iOS devices, we'll use a long press, and on the Mac, it'll be your user's secondary click setting.

So, a right click or a command click or two-finger tap.

The interaction is automatically integrated with other system behaviors like Drag and Drop.

So if you've adopted UIDragInteraction in your app and you choose to adopt UIContextMenuInteraction alongside it, users will be able to transition into dragging at any point.

So, here we see as soon as that initial threshold for dragging is met, you're able to move your finger to drag the photo over to the files app.

However, if you wait a little bit longer under the menu is presented, you're still able to transition smoothly into a drag without removing your finger from the screen.

So users have this nice fluid continuous experience.

The second part of our API package today is UIMenu and UIAction.

UIMenu and UIAction are a hierarchical menu construction system that allows us to define and describe the menu hierarchies that we're going to want to display.

UIMenus are composable.

They're basically like building blocks that we can put together in different configurations to describe different menu hierarchies.

So, in a simple example here, I've created a two action menu by creating a UIMenu and passing it to UIAction children, a share action and a delete action.

If I want to add a little bit more functionality to my menu by say adding a sub-menu, all I need to do is create another UIMenu, give it some descriptive title to display at the previous level in the hierarchy, and pass it the desired children.

So here I've chose to include a copy and a duplicate action.

So, when we present it, the menu looks something like this.

We have a Share action at the top, our edit action for our sub-menu in the middle, and a delete action at the bottom.

When the user taps edit, our sub-menu is presented, and our copy and duplicate actions pop in.

Adopting the interaction in your app is pretty straightforward.

If you've adopted other UIInteractions in your app like UIDragInteraction or some of the TextInteractions that are being introduced this year, this pattern should be really familiar to you.

The first thing well need to do is just create a UIContextMenuInteraction with some object as its delegate.

So, this could be our View Controller, for example.

Then we'll need to attach it to some view using UIView's addInteraction method.

Once we've done that, we'll need to conform to the interaction's delegate protocol.

The protocol has a single required method that is called when the interaction is about to begin.

It's the app's job at this point to either return a configuration to start the interaction or return nil to indicate that there's no menu to be presented for the current location.

The configuration object is how we're going to describe our menu.

It has an identifier.

The identifier is optional.

It's for you to use to be able to identify the interaction throughout its lifecycle, throughout the various delegate callbacks that you might get.

It also has a previewProvider and action provider closure.

Both of these are called when the menu is about to be presented.

This allows us to defer the potentially expensive work of actually building up the menu and the preview until they're actually needed.

So, in the example that we just looked at, our menu doesn't have that large rich preview.

It just has some actions, so we're going to focus on creating an actionProvider in our implementation of the delegate method.

So the actionProvider, when called, has a list of suggested actions that are passed to it by the system.

This can be a mix of UIMenus and UIActions, so potentially a fully constructed hierarchy that are picked up from the system.

These could be things that you defined in your responder chain using the new UI command API being introduced in iOS 13 or things that are offered by other system components.

So we're doing a fully custom menu here, so we're going to put the suggested actions aside.

First, we'll create our edit menu.

We'll just create a UIMenu with the title Edit, and we'll pass the two children that want it to have, a UIAction, with the title Copy, and a UIAction with the title Duplicate.

Then we'll define our root menu.

It often doesn't make sense for a root menu to have a title, so we'll skip it here.

However, if you do want to pass it a title, it'll be displayed as a header above the actions list.

And we'll pass it three children here, a share action, the edit menu that we just created, and finally a delete action with a destructive style to indicate to our users that the action has destructive consequences.

Finally, we'll create a configuration with some identifier and pass it our action provider.

And that's it.

That's really all it takes to create and display menus using UIContextMenuInteraction.

But with a little bit more work, we can take advantage of some of the animation customization APIs offered by the interaction delegate to build some really great, highly polished animations that really communicate meaning to our users.

So, in this example here, I've taken the same menu that we had earlier, and I've used the source presentation customization API to make the menu appear from the cell's icon instead of from the entire cell, and I've even customized the background color and even given the preview a nice circular shape.

These customization APIs are built on a UITargetedPreview.

This is a new API that's being introduced in iOS 13.

Now, if you've adopted UIDragInteraction and UIDropInteraction, and you built some custom Drag or Drop Previews in your app, then this is probably very familiar.

We've essentially taken UITargetedDragPreview and generalized it into a more general API that can be used by other interactions.

UITargetedDragPreview is now a subclass of UITargetedPreview.

So, if you already have meaningful drag previews that make sense for your menus because they're triggered from the same item, then you can reuse that code without doing any extra work.

To make adopting this in your apps even easier, to make it easier for you to bring up menus in some of the most common cases, we're also introducing some convenience API for UICollectionView and UITableView.

All that's needed to bring up a menu from your app, from your Table View on all the platforms that your app runs on is to implement a single extra method on its delegate.

Here, you'll once again return a configuration if you have a menu to display or return nil to indicate that there's nothing to be shown.

So, out of the box, these convenience APIs give you some polished default animations with menus presenting from Table or Collection View cells and then returning back to them when dismissed.

We're also providing the same custom presentation and dismissal hooks so you could build your custom appearance and disappearance animations.

Now, you're probably thinking to yourself, this sounds a lot like Peek and Pop in some ways.

Well, we noticed that too.

However, since this new API provides a much larger feature set and works on multiple platforms, we are deprecating UIViewController previewing in iOS 13.

So, go and replace your implementation of Peek and Pop with UIContextMenuInteraction and give your users a consistent experience in your app across all devices.

You might also want to consider replacing any long-press driven menus in your apps with this new interaction to take advantage of the new feature set.

And if you have menus that you're currently displaying alongside Drag and Drop, this is a great fluid solution that has coordinated gestures and a fully integrated experience that your users are going to come to expect starting in iOS 13.

All right, so now that you're equipped with all this great new knowledge, you should go and get your apps ready for iOS 13.

Ensure that they're ready for the future by making them flexible, take advantage of the new bar appearance and presentation APIs to give your apps that polished modern look that users are going to come to expect.

Go build the search experience you've always wanted to build using the great new search APIs, and give your apps some great features with the new productivity gestures and context menus.

If you have any questions about these topics or anything else related to UIKit, please come to the labs tomorrow, and for additional information about the session, you can find it at the session's website.

Thanks for coming by, and I hope you enjoy the rest of the conference.

[ Applause ]

Apple, Inc. AAPL
1 Infinite Loop Cupertino CA 95014 US