Modal Presentation — sposób wyświetlania ekranu ponad bieżącym kontekstem z blokadą interakcji z poprzednim interfejsem. W tworzeniu aplikacji mobilnych okna modalne są używane do zadań wymagających skupienia: wprowadzanie danych, potwierdzanie działań, autoryzacja i wybór opcji. Według Apple HIG, 2025, prezentacje modalne powinny zajmować nie więcej niż 20% scenariuszy nawigacyjnych w aplikacji. W Android modalność jest realizowana przez DialogFragment, BottomSheet i Activity z określonymi flagami launch.
Najważniejsze
Modal Presentation — to wzorzec nawigacji, w którym nowy ekran pojawia się nad bieżącym, tymczasowo blokując interakcję z rodzicielskim kontekstem. Użytkownik musi jawnie zakończyć zadanie modalne (anulowanie, zapisanie, zamknięcie), aby wrócić do poprzedniego stanu.
Modalność rozwiązuje zadanie poznawcze: skupia uwagę użytkownika na jednej czynności bez rozpraszania resztą interfejsu. Jest to krytycznie ważne dla formularzy rejestracyjnych, okien dialogowych potwierdzenia, wyboru plików i autoryzacji przez zewnętrzne serwisy. Human Interface Guidelines Apple zalecają używanie modalności tylko dla zadań wymagających ukończenia przed kontynuowaniem pracy.
W przeciwieństwie do modalnych okien internetowych, mobilna Modal Presentation może być pełnoekranowa (zajmować cały ekran) lub częściowa (Page Sheet, Bottom Sheet). Wybór typu zależy od kontekstu zadania i platformowych konwencji. iOS skłania się ku Page Sheet dla większości scenariuszy, pozostawiając Full Screen dla odtwarzaczy wideo i edytorów zdjęć.
Push Presentation (nawigacja stosowa) dodaje ekran do stosu nawigacyjnego i automatycznie pokazuje przycisk wstecz. Użytkownik może wrócić do poprzedniego ekranu w dowolnym momencie. Modal Presentation, przeciwnie, zakłada jawne zakończenie: przycisk wstecz albo nie istnieje, albo zamyka okno modalne, a nie wraca do poprzedniego ekranu.
Główne różnice między prezentacją modalną a push: Modal Presentation blokuje nawigację wstecz bez utraty danych, wymaga działania w celu zamknięcia (Save, Cancel, Done) i zazwyczaj przedstawia oddzielne zadanie. Push Presentation zachowuje hierarchię nawigacji, automatycznie dodaje przycisk powrotu i nadaje się do sekwencyjnego przeglądania treści.
| Cecha | Modal Presentation | Push Presentation |
|---|---|---|
| Blokada wstecz | Tak, wymagane jawne działanie | Nie, przycisk wstecz zawsze dostępny |
| Typowe zastosowanie | Formularze, autoryzacja, wybór | Szczegółowy przegląd, nawigacja |
| Animacja | Z dołu do góry (iOS), przesunięcie (Android) | Z prawej do lewej (iOS) |
| Stos nawigacji | Nie dodawany do głównego stosu | Dodawany do stosu |
W praktyce wybór między Modal a Push zależy od kontekstu. Zaleca się używanie modalności dla zadań, które użytkownik musi ukończyć przed kontynuowaniem, a Push — do sekwencyjnego przeglądania treści. Mieszanie wzorców na jednym ekranie prowadzi do zamieszania i pogarsza UX.
iOS oferuje kilka stylów prezentacji modalnej przez wyliczenie UIModalPresentationStyle. UIKit obsługuje .fullScreen (pełny ekran), .pageSheet (karta z odstępem u góry), .formSheet (wyśrodkowane okno na iPad) i .automatic (wybór przez system na podstawie kontekstu). Od iOS 13 domyślnym stylem stał się .automatic, który dla iPhone wybiera .pageSheet.
Podstawowa metoda UIKit dla prezentacji modalnej — present(_:animated:completion:). Kontroler wywołujący metodę staje się presentingViewController, a nowy — presentedViewController. Zamknięcie wykonuje się przez dismiss(animated:completion:). SwiftUI udostępnia modyfikator .sheet dla analogicznego zachowania.
Deklaratywne podejście SwiftUI używa modyfikatorów .sheet i .fullScreenCover. Pierwszy tworzy Page Sheet, drugi — pełnoekranową prezentację modalną. Oba przyjmują binding na Bool lub identyfikowalny obiekt, który zarządza widocznością okna modalnego. Zamknięcie następuje przy ustawieniu binding na false lub wywołaniu dismiss z environment.
struct ContentView: View {
@State private var showModal = false
var body: some View {
Button("Otwórz formularz") {
showModal = true
}
.sheet(isPresented: $showModal) {
RegistrationForm()
}
}
}
struct RegistrationForm: View {
@Environment(\.dismiss) private var dismiss
var body: some View {
Button("Zapisz") { dismiss() }
}
}
Android nie ma jednego API do prezentacji modalnej jak iOS. Zamiast tego platforma oferuje kilka mechanizmów: DialogFragment dla okien dialogowych, BottomSheetDialogFragment dla paneli dolnych i Activity z flagami NEW_TASK i CLEAR_TOP dla ekranów modalnych. W Jetpack Compose pojawił się jednolity komponent Dialog dla wszystkich typów okien modalnych.
DialogFragment — podstawowa klasa dla okien modalnych w Android SDK. Zarządza cyklem życia okna dialogowego, obsługuje obrót ekranu i zapisuje stan. Fragment wyświetla się nad Activity bez blokowania stosu nawigacyjnego. Zamknięcie wykonuje się przez dismiss() lub kliknięcie poza obszarem okna dialogowego, jeśli setCancelable(true).
BottomSheetDialogFragment wyświetla okno modalne jako panel podnoszący się z dołu. Ten wzorzec jest popularny w Material Design do wyboru opcji, udostępniania i szybkich działań. BottomSheet może mieć stałą wysokość lub być rozszerzalny (peek height + full height). W Compose używany jest ModalBottomSheet z biblioteki Material3.
@Composable
fun ModalScreen(onDismiss: () -> Unit) {
Dialog(onDismissRequest = onDismiss) {
Card(
modifier = Modifier.padding(16.dp)
) {
Column {
Text("Formularz modalny", style = MaterialTheme.typography.headlineSmall)
Button(onClick = onDismiss) {
Text("Zamknij")
}
}
}
}
}
Okna modalne — potężne narzędzie UX, ale ich nadmierne używanie pogarsza doświadczenie użytkownika. Apple HIG i Google Material Design zgadzają się w zaleceniach: modalność powinna być stosowana do zadań wymagających skupienia i nie przekraczać 20% ogólnej liczby działań nawigacyjnych.
Okna modalne nadają się do scenariuszy: wprowadzanie danych (formularze rejestracyjne, profilu), potwierdzanie (usuwanie, wysyłanie), wybór (picker daty, menedżer plików) i autoryzacja (OAuth, Firebase Auth). Jeśli zadanie zajmuje mniej niż 30 sekund i wymaga blokady kontekstu — wybieraj modalność.
Nie używaj prezentacji modalnej do: sekwencyjnego przeglądania treści (użyj Push), wyświetlania błędów (użyj Toast lub Snackbar), reklam i ofert promocyjnych bez jawnego żądania użytkownika. Material Design zaleca unikanie zagnieżdżonych okien modalnych — dezorientuje to użytkownika i narusza hierarchię nawigacji.
Dla okien modalnych z polami tekstowymi należy obowiązkowo obsługiwać utratę focusu klawiatury. Przy pojawieniu się klawiatury okno modalne powinno przesuwać się w górę, aby użytkownik widział wpisywany tekst. UIKeyboardWillShowNotification w iOS i adjustResize w Android rozwiązują to zadanie.
Rozważmy implementację prezentacji modalnej na obu platformach. Swift przykład pokazuje konfigurację UIModalPresentationStyle.pageSheet z delegatem do obsługi zamknięcia. Kotlin przykład demonstruje DialogFragment z niestandardowym layoutem i zapisywaniem stanu.
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("Okno modalne zamknięte")
}
}
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()
}
}
}
Często zadawane pytania
Używaj Modal Presentation do zadań wymagających skupienia, które użytkownik musi ukończyć przed kontynuowaniem: formularze, potwierdzenia, autoryzacja. Push nadaje się do sekwencyjnego przeglądania treści, gdzie użytkownik może swobodnie wracać. Okno modalne nie powinno zawierać nawigacji wewnątrz siebie.
Od iOS 13 domyślny styl .automatic wybiera .pageSheet dla iPhone. .pageSheet nadaje się do większości scenariuszy (formularze, szczegóły). .fullScreen — do treści multimedialnych (wideo, edytory zdjęć). .formSheet — dla aplikacji na iPad, gdzie potrzebne jest wyśrodkowane okno.
Jetpack Compose udostępnia komponent Dialog dla prostych okien modalnych i ModalBottomSheet dla paneli dolnych. Dialog przyjmuje onDismissRequest i treść w stylu Compose. Do zapisywania stanu używaj rememberSaveable wewnątrz okna dialogowego.
Apple HIG i Material Design nie zalecają zagnieżdżonych okien modalnych. Jeśli użytkownik otwiera okno modalne na innym oknie modalnym, traci kontekst i może się pogubić w hierarchii. Zamiast zagnieżdżania używaj Step Indicator lub wzorca Wizard z jednym oknem modalnym.
Używaj UIAdaptivePresentationControllerDelegate w iOS (metoda presentationControllerShouldDismiss) lub OnBackPressedDispatcher w Android. W przypadku niezapisanych danych pokazuj AlertDialog z opcjami: zapisz, anuluj zmiany, zostań na ekranie. Zapobiega to przypadkowej utracie danych przez użytkownika.
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również