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("Отваряне на формуляр") {
showModal = true
}
.sheet(isPresented: $showModal) {
RegistrationForm()
}
}
}
struct RegistrationForm: View {
@Environment(\.dismiss) private var dismiss
var body: some View {
Button("Запазване") { 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("Модален формуляр", style = MaterialTheme.typography.headlineSmall)
Button(onClick = onDismiss) {
Text("Затваряне")
}
}
}
}
}
Модалните прозорци — мощен 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("Модалният прозорец е затворен")
}
}
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 г. Ще ви консултираме и ще предложим най-доброто решение.
Прочетете също