Modal Presentation — способ отображения экрана поверх текущего контекста с блокировкой взаимодействия с предыдущим интерфейсом. В мобильной разработке модальные окна используются для фокусных задач: ввод данных, подтверждение действий, авторизация и выбор опций. По данным Apple HIG, 2025, модальные презентации должны занимать не более 20% навигационных сценариев в приложении. В Android модальность реализуется через DialogFragment, BottomSheet и Activity с определёнными launch-флагами.
Главное
Modal Presentation — это паттерн навигации, при котором новый экран появляется поверх текущего, временно блокируя взаимодействие с родительским контентом. Пользователь должен явно завершить модальную задачу (отмена, сохранение, закрытие), чтобы вернуться к предыдущему состоянию.
Модальность решает когнитивную задачу: она фокусирует внимание пользователя на одном действии без отвлечения на остальной интерфейс. Это критически важно для форм регистрации, диалогов подтверждения, выбора файлов и авторизации через сторонние сервисы. Human Interface Guidelines Apple рекомендуют использовать модальность только для задач, которые требуют завершения до продолжения работы.
В отличие от веб-модальных окон, мобильная Modal Presentation может быть полноэкранной (занимает весь экран) или частичной (Page Sheet, Bottom Sheet). Выбор типа зависит от контекста задачи и платформенных соглашений. iOS тяготеет к Page Sheet для большинства сценариев, оставляя Full Screen для видеоплееров и фоторедакторов.
Push Presentation (стековая навигация) добавляет экран в навигационный стек и автоматически показывает кнопку назад. Пользователь может вернуться на предыдущий экран в любой момент. Modal Presentation, напротив, предполагает явное завершение: кнопка назад либо отсутствует, либо закрывает модальное окно, а не возвращает на предыдущий экран.
Основные различия между модальной и push-презентацией: Modal Presentation блокирует навигацию назад без потери данных, требует действия для закрытия (Save, Cancel, Done) и обычно представляет отдельную задачу. Push Presentation сохраняет иерархию навигации, автоматически добавляет кнопку возврата и подходит для последовательного просмотра контента.
| Характеристика | Modal Presentation | Push Presentation |
|---|---|---|
| Блокировка назад | Да, требуется явное действие | Нет, кнопка назад всегда доступна |
| Типичное использование | Формы, авторизация, выбор | Детальный просмотр, навигация |
| Анимация | Снизу вверх (iOS), слайд (Android) | Справа налево (iOS) |
| Стек навигации | Не добавляется в основной стек | Добавляется в стек |
На практике выбор между Modal и Push зависит от контекста. Рекомендуется использовать модальность для задач, которые пользователь должен завершить перед продолжением, и Push — для последовательного изучения контента. Смешивание паттернов на одном экране приводит к путанице и ухудшает UX.
iOS предлагает несколько стилей модальной презентации через перечисление UIModalPresentationStyle. UIKit поддерживает .fullScreen (полный экран), .pageSheet (карточка с отступом сверху), .formSheet (центрированное окно на iPad) и .automatic (выбор системой на основе контекста). Начиная с iOS 13, дефолтным стилем стал .automatic, который для iPhone выбирает .pageSheet.
Базовый метод UIKit для модальной презентации — present(_:animated:completion:). Контроллер, вызывающий метод, становится presentingViewController, а новый — presentedViewController. Закрытие выполняется через dismiss(animated:completion:). SwiftUI предоставляет модификатор .sheet для аналогичного поведения.
Декларативный подход SwiftUI использует модификаторы .sheet и .fullScreenCover. Первый создаёт Page Sheet, второй — полноэкранную модальную презентацию. Оба принимают binding на Bool или идентифицируемый объект, который управляет видимостью модального окна. Закрытие происходит при установке binding в false или вызове dismiss из environment.
struct ContentView: View {
@State private var showModal = false
var body: some View {
Button("Open Form") {
showModal = true
}
.sheet(isPresented: $showModal) {
RegistrationForm()
}
}
}
struct RegistrationForm: View {
@Environment(\.dismiss) private var dismiss
var body: some View {
Button("Save") { dismiss() }
}
}
Android не имеет единого API для модальной презентации, как iOS. Вместо этого платформа предлагает несколько механизмов: DialogFragment для диалоговых окон, BottomSheetDialogFragment для нижних панелей и Activity с флагами NEW_TASK и CLEAR_TOP для модальных экранов. В Jetpack Compose появился единый компонент Dialog для всех типов модальных окон.
DialogFragment — базовый класс для модальных окон в Android SDK. Он управляет жизненным циклом диалога, обрабатывает поворот экрана и сохраняет состояние. Фрагмент отображается поверх Activity без блокировки навигационного стека. Закрытие выполняется через dismiss() или нажатие вне области диалога, если setCancelable(true).
BottomSheetDialogFragment отображает модальное окно в виде панели, поднимающейся снизу. Этот паттерн популярен в Material Design для выбора опций, шаринга и быстрых действий. BottomSheet может быть фиксированной высоты или расширяемой (peek height + full height). В Compose используется ModalBottomSheet из библиотеки Material3.
@Composable
fun ModalScreen(onDismiss: () -> Unit) {
Dialog(onDismissRequest = onDismiss) {
Card(
modifier = Modifier.padding(16.dp)
) {
Column {
Text("Modal Form", style = MaterialTheme.typography.headlineSmall)
Button(onClick = onDismiss) {
Text("Close")
}
}
}
}
}
Модальные окна — мощный инструмент UX, но их чрезмерное использование ухудшает пользовательский опыт. Apple HIG и Google Material Design сходятся в рекомендациях: модальность должна применяться для фокусных задач и не превышать 20% от общего числа навигационных действий.
Модальные окна подходят для сценариев: ввод данных (формы регистрации, профиля), подтверждение (удаление, отправка), выбор (пикер даты, файловый менеджер) и авторизация (OAuth, Firebase Auth). Если задача занимает менее 30 секунд и требует блокировки контекста — выбирайте модальность.
Не используйте модальную презентацию для: последовательного просмотра контента (используйте Push), отображения ошибок (используйте Toast или Snackbar), рекламы и промо-предложений без явного запроса пользователя. Material Design рекомендует избегать вложенных модальных окон — это дезориентирует пользователя и нарушает иерархию навигации.
Для модальных окон с текстовыми полями обязательно обрабатывайте потерю фокуса клавиатуры. При появлении клавиатуры модальное окно должно сдвигаться вверх, чтобы пользователь видел вводимый текст. UIKeyboardWillShowNotification в iOS и adjustResize в Android решают эту задачу.
Рассмотрим реализацию модальной презентации на обеих платформах. Swift пример показывает настройку UIModalPresentationStyle.pageSheet с делегатом для обработки закрытия. Kotlin пример демонстрирует DialogFragment с кастомным layout и сохранением состояния.
let modalVC = ModalViewController()
modalVC.modalPresentationStyle = .pageSheet
if let sheet = modalVC.sheetPresentationController {
sheet.detents = [.medium(), .large()]
sheet.prefersGrabberVisible = true
}
modalVC.presentationController?.delegate = self
present(modalVC, animated: true)
// MARK: - UIAdaptivePresentationControllerDelegate
extension ViewController: UIAdaptivePresentationControllerDelegate {
func presentationControllerDidDismiss(_ presentationController: UIPresentationController) {
print("Modal dismissed")
}
}
class ModalDialogFragment : DialogFragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_modal, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
view.findViewById<Button>(R.id.closeButton).setOnClickListener {
dismiss()
}
}
}
Часто задаваемые вопросы
Используйте Modal Presentation для фокусных задач, которые пользователь должен завершить перед продолжением: формы, подтверждения, авторизация. Push подходит для последовательного просмотра контента, где пользователь может свободно возвращаться назад. Модальное окно не должно содержать навигации внутри себя.
Начиная с iOS 13, дефолтный стиль .automatic выбирает .pageSheet для iPhone. .pageSheet подходит для большинства сценариев (формы, детали). .fullScreen — для медиаконтента (видео, фоторедакторы). .formSheet — для iPad-приложений, где нужно центрированное окно.
Jetpack Compose предоставляет компонент Dialog для простых модальных окон и ModalBottomSheet для нижних панелей. Dialog принимает onDismissRequest и контент в Compose-стиле. Для сохранения состояния используйте rememberSaveable внутри диалога.
Apple HIG и Material Design не рекомендуют вложенные модальные окна. Если пользователь открывает модальное окно поверх другого модального окна, он теряет контекст и может запутаться в иерархии. Вместо вложенности используйте Step Indicator или Wizard-паттерн с одним модальным окном.
Используйте UIAdaptivePresentationControllerDelegate в iOS (метод presentationControllerShouldDismiss) или OnBackPressedDispatcher в Android. При наличии несохранённых данных показывайте AlertDialog с вариантами: сохранить, отменить изменения, остаться на экране. Это предотвращает случайную потерю данных пользователем.
Итоги
Мы разработаем мобильное приложение под ключ
IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.
Читайте также