Modal presentation in iOS

Ryuichi / Rick
7 min readFeb 14, 2021

--

Photo by Vladimir Haltakov on Unsplash

This article describes the basics of the standard iOS modality and differences around iOS13.

What is the “Modality”

Modality is a design technique that presents content in a temporary mode that’s separate from the user’s previous current context and requires an explicit action to exit. Presenting content modally can:

- Help people focus on a self-contained task or set of closely related options
- Ensure that people receive and, if necessary, act on critical information

https://developer.apple.com/design/human-interface-guidelines/ios/app-architecture/modality/

What are the differences around iOS 13?

  1. The display format of a sheet
  2. The way of controlling a modal dismiss

So, let’s dive into the modality!!

modalPresentationStyle: UIModalPresentationStyle

modalPresentationStyle is the property of UIViewController. By setting this property, you can specify the display format of the presented view controller.

Each behavior of UIModalPresentationStyle

.fullScreen

Default value under iOS13.

As its name implies, a view controller is displayed in full screen regardless of the display format before transition and size class.

.pageSheet

A view controller is displayed centered in the presentation view, and the size of the presented view is optimized by several factors such as the screen size, orientation and font size. And if you want to resize the presented view, use .formSheet.

display format
The display format differs depending on the size class and iOS version.

  • iOS13 or later
  • under iOS13

※ You can check the size classes of each device here.
https://developer.apple.com/design/human-interface-guidelines/ios/visual-design/adaptivity-and-layout/

How to dismiss(iOS13 or later)
The way of dismissing the presented view depends on the display format.
Under iOS13 or at fullScreen, to dismiss the presented view, you have to use some handlers such as UIButton’s action.

left: iPhoneXs(iOS14.2), center: iPad9.7-inch(iOS14.2), right: iPad9.7-inch(iOS12.0)

.formSheet

Most features are the same as .pageSheet with a few differences.

display format

  • iOS13 or later
    Same as .pageSheet.
  • under iOS13

※ Even though display format is the centered sheet in a regular-width, regular-height size class, you cannot dismiss the presented view without any handlers under iOS13.

Resize the presented view
As mentioned above, you can resize the presented view unlike .pageSheet.
(The default content size is smaller than .pageSheet.)

You can resize the presented view by setting var preferredContentSize: CGSize { get set }, which is property of UIViewController.

left: Pad9.7-inch(iOS12.0), right: iPad9.7-inch(iOS14.2)

.currentContext

Style for displaying over another view controller’s content. And, you can select which view to display on.

Size of the presented view
Same as the presenting view’s size.

Which view to display on
You can select which view to display on by setting var definesPresentationContext: Bool { get set }, which is property of UIViewController. A view controller is displayed over the view controller whose definesPresentationContext is true. If there is no view controller whose definesPresentationContext is true, UIKit asked to root view controller of the window. And also, the default value of definesPresentationContext is false, but true is set by default for some subclass of UIViewController such as UINavigationController and UITabBarController.

Do not use to display modal content
If you are using .currentContext to display modal content by presenting over the view whose display style is modal, it is better to use another style such as .pageSheet, .formSheet. You should use .currentContext to present a view over the view whose display style is full screen. This style is useful for determining which view to display on.

If you use the current context modal view style to display modal content within a split view pane, popover, or other view that isn’t fullscreen, you should switch to using a sheet when presenting modal content in a compact environment.

When presenting a view controller in a popover

When presenting a view controller in a popover, this presentation style is supported only if the transition style is UIModalTransitionStyle.coverVertical. Attempting to use a different transition style triggers an exception. However, you may use other transition styles (except the partial curl transition) if the parent view controller is not in a popover.

https://developer.apple.com/documentation/uikit/uimodalpresentationstyle/currentcontext

.custom

I won’t describe too much here.
Custom modal can be implemented by returning an instance of the subclass of UIPresentationController by the presentationController(forPresented:presenting:source:) , which is the delegate method of UIViewControllerTransitioningDelegate.

.overFullScreen

Basically, same as .fullScreen, but if you use .overFullScreen and not set the presented view controller to be opaque, the presenting view can be transparent. Conversely, if you use .fullScreen, the presenting view cannot be transparent. That’s because the views belonging to the presenting view controller are removed after the presentation completes. The removed views will come back when the presented view will be attempted to be dismissed.

For example, if ViewController1 presents ViewController2 in two ways, .fullScreen and .overFullScreen, you can find that ViewController1(= the presenting controller) was removed in case of .fullScreen.

left: .fullScreen, right: .overFullScreen

.overCurrentContext

Basically, same as .currentContext, but if you use .overCurrentContext and not set the presented view controller to be opaque, the presenting view can be transparent. The reason is the same as described about overFullScreen.

.popover

You have to set sourceView and sourceRect, which are the property of presented view controller’s popoverPresentationController. Otherwise, a run-time error occurs like below.

Thread 1: “UIPopoverPresentationController (<UIPopoverPresentationController: 0x7fab45d06000>) should have a non-nil sourceView or barButtonItem set before the presentation occurs.”

Whether to show popover on iPhone
If you want to show popover not only on the iPad, but also iPhone, you must return UIModalPresentationStyle.none by adaptivePresentationStyle(for:traitCollection:), which is the method
UIPopoverPresentationControllerDelegate. If you do not so, the display format will be same as .formSheet on iPhone.

Whether to allow popover to be dismissed by tapping outside of popover

Specify the direction of the arrow
You can determine the direction of arrow by setting var permittedArrowDirections: UIPopoverArrowDirection { get set }, which is property of UIPopoverPresentationController. The default value is .any, but if so, the system determines the direction of arrow automatically. So, it is better to determine by yourself. And, you can prevent the arrow from appearing by setting [].

// up arrow
vc.popoverPresentationController?.permittedArrowDirections = .up
left: iPhoneXs(iOS14.2), right: iPad9.7-inch(iOS14.2)

.none

It is not used for anything other than the return value of the UIAdaptivePresentationControllerDelegate method.

.automatic

Default value in iOS13 or later. It will be .pageSheet normally, but there are exceptions.
e.g.

let vc = UIImagePickerController()
vc.sourceType = .photoLibrary
vc.modalPresentationStyle // .fullScreen

Control the behavior of modal dismiss

Mainly, there are 3 ways.

1. Control by isModalInPresentation (only in iOS13 or later)
Only in iOS13 or later, you can use var isModalInPresentation: Bool { get set}, which is the property of UIViewController. By setting isModalInPresentation to true(default value is false), you can prevent presented view controller from dismissing by down swipe and tapping outside of the presented view controller.

if #available(iOS 13.0, *) {
isModalInPresentation = true
}

2. Control by delegate method(only in iOS13 or later)
If isModalInPresentation is not true, you can control by presentationControllerShouldDismiss(_:), which is the delegate method of
UIAdaptivePresentationControllerDelegate. And also, if you don’t want to dismiss the view by a down swipe, for example, on which view, users can enter text, you can display an alert when you try to down swipe.

Relationship between these APIs

Relationship between APIs about modality

3. Adding handlers such as UIButton’s action or UITapGestureRecognizer

--

--