BottomSheet in iOS 15: ​​UISheetPresentationController and its capabilities

Dmitry Demyanov
Surf
Published in
7 min readDec 15, 2021

Back before iOS 15, developers had no fast and easy ways to show content in a form of a sheet that would only take up a part of the screen. All we could do is invent our own ways of implementing this modal presentation.

However, in the current version of the iOS UI Kit, we finally have a native BottomSheet functionality (even though it’s not an official term according to Apple).

Hi! I’m Dmitry Demianov, an iOS developer at Surf. In this article I’ll tell you about:

  • the way ​​UISheetPresentationController interacts with the keyboard, gestures inside the sheet, and background controllers.
  • extra customization options currently available to developers.

How was BottomSheet implemented before iOS 15?

Previously we had to use third-party libraries or create our own implementations. Both of the options had their drawbacks.

Third-party libraries. Here we were often limited by the customization options at hand. One way to solve that is to fork the repository containing the library and add the changes you need. This was the most common choice made in the projects I came across.

Custom implementation. In this case, we’re free to do what we want, but the nuances take too much time to add. The solution has to be reusable, so the choice that makes sense is to divide the whole thing into a container controller that is responsible for presentation, and a controller with the content that has to go inside the first container.

Here’s one way to implement that:

  • UIViewControllerTransitioningDelegate — a protocol containing methods that return the objects, defining the custom modal presentation.
  • UIViewControllerAnimatedTransitioning tells the system to animate the bottom sheet sliding up and down.
  • UIViewControllerInteractiveTransitioning responds to touch events enabling users to expand the bottom sheet or swipe it down.
  • UIPresentationController defines presentation parameters such as size or background dimming.

The thing worth noting here is the presentation size. It can either be fixed or calculated via AutoLayout. What’s more, it can be changed after it’s already displayed.

And here’s something we should also keep in mind:

  • Keyboard interactions.
  • Interactions between the swiping gestures that expand or remove the bottom sheet and scrolling gestures inside ContentViewController, say, when users scroll through a list or a collection.
  • Enabling users to interact with elements in the background.

All in all, it’s hardly a 20-minute in-and-out adventure and it’s the main drawback of this option.

What do we have in the iOS 15 UI Kit? UISheetPresentationController

Let’s get straight to the code.

This much is already enough to display a SheetViewController half the size of a screen. On the right, there’s a typical modal implementation just for the sake of comparison.

Detents. Medium and Large.

Let’s take a closer look at the code, namely, the detents property.

detent defines the height of the presentation containing the controller. The default value is [.large()], which corresponds to the typical full-screen modal presentation (the example on the right). In our code, we gave it a medium value. As a result, it takes up half the screen.

detents accepts a list of several values, i.e. you can write both options at one go.

This way users will be able to change the size of the sheet by swiping it up and down.

The Apple docs say that detents values should be specified in descending order. Otherwise, we get the following:

  • The controller goes full-screen straight away.
  • We no longer have a stack of controllers at the top of the screen.

Is it a bug or a feature? The first one is definitely something you don’t want. If you actually need the sheet to slide up to full-screen right away, all you need to do is put sheet.selectedDetentIdentifier = .large after you write the list in detents. Not having the stack effect is quite a questionable result as well. All in all, it’s not worth working against the guidelines.

You can also change the detent programmatically:

When creating the sheet, we wrote sheet.detents = [.medium(), .large()]. Therefore, the sheet will first go up to the middle of the screen and then go fullscreen once the changeHeight() is called.

However… it doesn’t happen. In fact, the above code doesn’t work at all, and it’s all about oldValue. The selectedDetentIdentifier has a default value of nil.

There are two ways to fix that:

  • Specify the initial value when you create the sheet.
  • Assign the default value to oldValue. In this case, it’s medium, since, once opened, the sheet picks the smallest possible size value:

Now the height of our sheet can actually be changed programmatically. In addition to that, the value will always stay relevant: once a user swipes the sheet up or down, selectedDetentIdentifier gets updated.

However, I doubt anyone would like the way this transition looks without animation. Thankfully, it’s really easy to fix. Simply add any changes you need to make animated to the animateChanges block:

Now, that’s much better.

Custom detents values

Now let’s have a look at other detents values available. Oh, wait! There aren’t any… Can it be that medium and large are the only options? I mean, this list is clearly begging for the small value. That way we could choose between quarter-of-the-screen, half-screen, and full-screen modes.

Unfortunately, we’ll have to deal with it: if you need your bottom sheet to take up less than half the screen, you can forget about UISheetPresentationController for now.

I’m hoping we’ll see the small value in the upcoming iOS — after all, it seems like a perfect addition. Not to mention that having a list of values clearly implies that the list is going to be expanded.

If you want the size of your bottom sheet to automatically match the amount of content it shows, UISheetPresentationController won’t be of any use. In that case, you’ll once again be left with the choice between a third-party library and a custom implementation.

Keyboard interaction

Whenever a keyboard slides up, the sheet height automatically changes to large. That’s handy!

And what if we don’t put large in the list of available detents when we create the sheet? The sheet will still go up but not to the fullscreen mode. Instead, its height will increase by keyboard’s height value.

So detent can actually have some values other than medium and large after all. But for now, it only works when you use the keyboard.

Interaction with the gestures inside the sheet

Let’s check if the gesture we use to swipe the sheet up or down in any way clashes with the gestures we use inside the sheet: e.g. scrolling through a list.

As long as we have only one detents value, nothing goes wrong. We can see a smooth transition between scrolling the content and swiping down the sheet.

If both detents are available, swipe gestures are given priority over scrolling. You can change it as follows:

Now when you scroll through the table, it behaves the same way as it did in the previous example with a single detent available. And you can still swipe the sheet up or down if you drag it by an empty part.

Interaction with the background controller

Whenever the bottom sheet appears, the background is dimmed. Whenever you tap on it, the sheet slides down. That’s predictable and logical, but with UISheetPresentationController you can avoid using dimmingView. As a result, users will be able to interact with both the bottom sheet and the background.

In this property, you set the maximum sheet height that still allows for interaction with background elements. So far the only value that makes sense is medium. However, it also seems to be hinting at the list of detents being extended in the upcoming iOS updates.

Additional customization options

You can also show or hide the grabber view on top of the sheet and change the corner radius of the sheet.

But then again, no one said you can’t replace the grabber with a simple UIView and customize it any way you like. For example, change its color or thickness, or add some space between the grabber and the upper edge of the sheet.

When is UISheetPresentationController worth using?

So far you can use it anytime the meager choice of detents doesn’t bother you. Apart from that, it’s a handy and flexible implementation. For one thing, it fits the system apps pretty well: e.g. the list of favorites in Safari.

I’m sure the following iOS updates will provide more detents, turning it into a comprehensive time-saving solution.

--

--