Inactive — přechodový stav životního cyklu aplikace mezi Active a Background, ve kterém je aplikace viditelná na obrazovce, ale nepřijímá dotykové události. Vysvětlujeme, jak vzniká Inactive na iOS a Android, které metody delegáta za něj odpovídají a jak správně zpracovávat přerušení — hovory, oznámení a systémová gesta.
Hlavní body
Inactive — přechodový stav životního cyklu mobilní aplikace, který vzniká při přechodu mezi Active a Background. V tomto stavu je aplikace stále na popředí a viditelná uživateli, ale nepřijímá dotykové události, stisky kláves ani jiné UI události. Systém blokuje přenos událostí do aplikace, ale UI zůstává na obrazovce a není minimalizováno.
Povaha Inactive je dočasná. Tento stav trvá přesně tak dlouho, jak dlouho trvá systémové přerušení: od 0.1 sekundy při rychlém zavření Control Center po několik sekund při příchozím hovoru s obrazovkou hovoru. Po skončení přerušení se aplikace buď vrátí do Active, nebo přejde do Background, pokud uživatel přepnul na jinou aplikaci. Inactive je jediný stav, ze kterého je možný přechod oběma směry: zpět do Active nebo dále do Background.
Na iOS je Inactive spravován systémem automaticky. Vývojář nemůže prodloužit ani zkrátit dobu strávenou v Inactive — je zcela řízena UIApplication. Jediné, co může vývojář udělat, je správně zpracovat přechod do Inactive přes applicationWillResignActive a návrat přes applicationDidBecomeActive. Na Androidu je analogií onPause, i když sémantika se liší: onPause je volán i při částečném zakrytí Activity jinou komponentou.
Na iOS je Inactive samostatný stav životního cyklu aplikace (jeden z pěti: Not Running, Active, Inactive, Background, Suspended). Na Androidu neexistuje přímá analogie — onPause signalizuje, že Activity ztrácí vstupní fokus, ale může zůstat viditelné (například při otevření dialogu). Klíčový rozdíl: iOS Inactive je stav aplikace jako celku, Android onPause je stav konkrétního Activity. V režimu multi-window na Androidu může být jedno Activity v onPause (bez fokusu) a druhé v onResume (s fokusem).
| Vlastnost | iOS Inactive | Android onPause |
|---|---|---|
| UI viditelné | Ano | Ano (částečně nebo zcela) |
| Dotykové události | Nepřijímá | Nepřijímá |
| Doba trvání | Do konce přerušení | Do návratu fokusu nebo přechodu na pozadí |
| Další stav | Active nebo Background | onResume nebo onStop |
| Úroveň | Aplikace (UIApplication) | Activity |
| Multi-window | Jedna scéna aktivní | Více Activity v onPause |
Inactive na iOS vzniká v několika přesně definovaných scénářích. Uživatel vyvolá Control Center (tažení dolů z pravého horního rohu na iPhone X+ nebo tažení nahoru na starších modelech). Uživatel otevře Notification Center (tažení dolů z levého horního rohu). Přichází příchozí hovor — systém zobrazí obrazovku hovoru nad aplikací. Je vyžádáno systémové oprávnění — geolokace, mikrofon, kamera, kontakty. Na iPadu se spouští Slide Over nebo Split View — aktivní scéna se stává Inactive.
Na Androidu onPause (analogie Inactive) vzniká v ještě širším spektru situací. Otevření dialogového okna (AlertDialog, DialogFragment). Částečné zakrytí Activity jiným Activity (například průhledné Activity pro autentizaci). Otáčení obrazovky (Activity je znovu vytvořeno, sekvence: onPause → onStop → onDestroy → onCreate → onStart → onResume). Režim multi-window — neaktivní okno obdrží onPause. Každá z těchto událostí vyžaduje pozastavení operací náročných na zdroje pro úsporu baterie a výkonu.
import UIKit
extension Notification.Name {
static let systemInterruptionBegan = Notification.Name("systemInterruptionBegan")
static let systemInterruptionEnded = Notification.Name("systemInterruptionEnded")
}
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
func applicationWillResignActive(_ application: UIApplication) {
// Aplikace přechází do Inactive — systémové přerušení
print("Přerušení: Control Center, hovor nebo systémový alert")
// Pozastavení operací citlivých na čas
pauseVideoPlayback()
stopContinuousDataCollection()
hideSensitiveInformation()
// Upozornění komponent
NotificationCenter.default.post(name: .systemInterruptionBegan, object: nil)
}
// Návrat z Inactive do Active
func applicationDidBecomeActive(_ application: UIApplication) {
resumeVideoPlayback()
restartDataCollection()
NotificationCenter.default.post(name: .systemInterruptionEnded, object: nil)
}
private func pauseVideoPlayback() {
// Zastavení videa, aby se zvuk nepřekrýval
}
private func hideSensitiveInformation() {
// Skrytí citlivých dat při snímku obrazovky
// Control Center/App Switcher pořizují snímek UI
}
}Kód ukazuje zpracování Inactive v UIKit. applicationWillResignActive zastaví video, zastaví sběr dat a skryje citlivé informace. To je důležité, protože při otevření Control Center nebo App Switcher systém pořídí snímek aktuálního UI — uživatel může vidět důvěrná data v náhledu. NotificationCenter umožňuje komponentám aplikace přihlásit se k odběru událostí přerušení.
Na iOS je Inactive zpracováván dvojicí metod: applicationWillResignActive (přechod do Inactive) a applicationDidBecomeActive (návrat z Inactive). Tyto metody jsou součástí UIApplicationDelegate a jsou volány pro každý přechod přes Inactive. Od iOS 13 a UISceneDelegate k nim přibyly sceneWillResignActive a sceneDidBecomeActive pro multi-window scénáře.
Na iPadu s iOS 13+ může mít aplikace více scén (oken). Každá scéna má svůj vlastní životní cyklus. Jedna scéna se může stát Inactive (uživatel přepnul na jinou scénu), zatímco druhá zůstává Active. To je důležitý rozdíl oproti iPhonu, kde je Inactive globálním stavem pro celou aplikaci. Při vývoji pro iPad je třeba Inactive zpracovávat pro každou scénu zvlášť.
import UIKit
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
// Scéna se stává neaktivní
func sceneWillResignActive(_ scene: UIScene) {
// Na iPadu tato scéna ztrácí fokus, ale ostatní mohou být aktivní
print("Scéna ztrácí aktivitu")
// Pozastavení úkolů této scény
pauseSceneSpecificOperations()
}
// Scéna se stává aktivní
func sceneDidBecomeActive(_ scene: UIScene) {
print("Scéna se stala aktivní")
resumeSceneSpecificOperations()
}
private func pauseSceneSpecificOperations() {
// Pozastavení operací specifických pro tuto scénu
}
private func resumeSceneSpecificOperations() {
// Obnovení operací při návratu fokusu
}
}
// AppDelegate zůstává vstupním bodem, deleguje scénám
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(
_ application: UIApplication,
configurationForConnecting connectingSceneSession: UISceneSession,
options: UIScene.ConnectionOptions
) -> UISceneConfiguration {
return UISceneConfiguration(
name: "Default Configuration",
sessionRole: connectingSceneSession.role
)
}
}Kód ukazuje SceneDelegate pro zpracování Inactive na úrovni scény. sceneWillResignActive je voláno, když konkrétní okno ztrácí fokus — to může nastat při přepínání mezi okny na iPadu. AppDelegate konfiguruje UISceneConfiguration pro podporu multi-window. Každá scéna má nezávislý stav a vývojář by je měl zpracovávat odděleně.
Na Androidu je přímou analogií iOS Inactive metoda onPause() životního cyklu Activity. Je volána, když Activity ztrácí vstupní fokus, ale může zůstat viditelné. Typické scénáře: otevření dialogového okna, spuštění jiného Activity ve stejné aplikaci, příchozí hovor, stisk tlačítka Home nebo Recents. V onPause by měl vývojář pozastavit operace náročné na zdroje — animace, přehrávání videa, práci s kamerou.
Důležitý rozdíl na Androidu — onPause vždy předchází onStop, ale ne naopak. Activity může obdržet onPause bez onStop (například při otevření průhledného Activity). Také onPause může být voláno několikrát během života Activity — při každé změně fokusu. Neumisťujte do onPause jednorázovou logiku — použijte onStop pro finální operace a onPause pouze pro pozastavení interaktivních akcí.
class VideoPlayerActivity : AppCompatActivity() {
private var exoPlayer: ExoPlayer? = null
private var currentPosition: Long = 0L
override fun onPause() {
super.onPause()
// Aplikace ztrácí fokus — pozastavujeme video
exoPlayer?.let { player ->
if (player.isPlaying) {
currentPosition = player.currentPosition
player.pause()
}
}
// Skrýváme citlivá data (GDPR/bankovní obrazovky)
if (window.decorView.systemUiVisibility and
View.SYSTEM_UI_FLAG_SECURE == 0
) {
hideSensitiveOverlay()
}
}
override fun onResume() {
super.onResume()
// Návrat fokusu — obnovujeme přehrávání
exoPlayer?.seekTo(currentPosition)
exoPlayer?.play()
showSensitiveOverlay()
}
private fun hideSensitiveOverlay() {
// Překrýváme černou obrazovkou finanční data
}
}Kód ukazuje správné zpracování onPause pro přehrávač videa. ExoPlayer je pozastaven při ztrátě fokusu a pozice přehrávání je uložena. Při návratu do onResume přehrávač obnoví přehrávání z uložené pozice. Dále je ukázán vzor skrývání citlivých dat — důležitý pro finanční a lékařské aplikace vyžadující ochranu před snímky obrazovky při přepínání.
První pravidlo — skrývejte důvěrná data při přechodu do Inactive. Když uživatel otevře Control Center nebo App Switcher, iOS pořídí snímek aktuální obrazovky. Na Androidu podobně — systém zobrazí náhled posledního Activity v Recents. Použijte UIApplication.shouldSnapshotSecureApp (iOS 16+) nebo FLAG_SECURE (Android) pro ochranu důvěrných obrazovek.
Druhé pravidlo — pozastavujte animace a média. Inactive není nejvhodnější doba pro přehrávání videa nebo animací, protože je uživatel nevidí. Navíc přehrávání na pozadí může vést k překrývání zvuků se systémovými zvuky (hovor, oznámení). Zastavte AVPlayer, ExoPlayer a UIView.animate při přechodu do Inactive a obnovte při návratu do Active.
Třetí pravidlo — blokujte vstup dat. Pokud aplikace obsahuje vstupní formuláře nebo koncepty, zablokujte klávesnici a vstupní pole při přechodu do Inactive. To zabraňuje náhodnému vstupu při návratu a chrání před zachycením dat přes systémové překryvy. Na iOS vypněte first responder (view.endEditing(true)), na Androidu — vyčistěte fokus (currentFocus?.clearFocus()).
Čtvrté pravidlo — neprovádějte dlouhé operace v applicationWillResignActive nebo onPause. Tyto metody by měly být dokončeny ve zlomcích sekundy. Pokud potřebujete uložit velké množství dat, začněte ukládání na vlákně na pozadí a dokončete ho v applicationDidEnterBackground nebo onStop. iOS dává 5 sekund na provedení applicationWillResignActive, poté může systém aplikaci násilně ukončit.
import UIKit
final class SecureOverlayManager {
private var blurView: UIVisualEffectView?
func showBlurOverlay() {
guard let window = UIApplication.shared.keyWindow,
blurView == nil
else { return }
let blur = UIVisualEffectView(effect: UIBlurEffect(style: .dark))
blur.frame = window.bounds
blur.autoresizingMask = [.flexibleWidth, .flexibleHeight]
window.addSubview(blur)
blurView = blur
}
func removeBlurOverlay() {
blurView?.removeFromSuperview()
blurView = nil
}
}
// Použití v AppDelegate
func applicationWillResignActive(_ application: UIApplication) {
SecureOverlayManager().showBlurOverlay()
}
func applicationDidBecomeActive(_ application: UIApplication) {
SecureOverlayManager().removeBlurOverlay()
}Kód ukazuje implementaci bezpečného překryvu pro ochranu dat při přechodu do Inactive. UIVisualEffectView s efektem rozostření (blur) je umístěn přes celé UI při přechodu do Inactive a odstraněn při návratu do Active. To zaručuje, že důvěrná data nebudou viditelná na snímcích App Switcher a Control Center. Podobně lze použít UIImageView s logem pro brandovaný překryv.
Často kladené otázky
Ano. Inactive je povinný přechodový stav před přechodem do Background na iOS. Aplikace nemůže přejít přímo z Active do Background — nejprve se stane Inactive, poté Background. Na Androidu podobně: onPause je vždy volán před onStop. To dává vývojáři možnost připravit data k uložení před úplným přechodem na pozadí.
Ano. Na iPadu při spuštění Slide Over nebo Split View se aktivní scéna stává Inactive, i když nedochází k žádnému systémovému přerušení — uživatel jednoduše interaguje s jinou scénou. To je funkce multi-window iPadOS. Na iPhonu je Inactive vždy způsoben systémovým přerušením — hovorem, oznámením, Control Center nebo Notification Center.
Obvykle 0.1 až 2 sekundy. Při příchozím hovoru s obrazovkou hovoru — až 30 sekund (dokud uživatel neodpoví nebo neodmítne hovor). iOS čas v Inactive násilně neomezuje, ale systém může aplikaci ukončit, pokud nereaguje na události (watchdog). Na Androidu onPause nemá časové omezení, ale doporučuje se dokončit práci do 200 ms.
ScenePhase.inactive — hodnota enum ScenePhase, nastavená když je scéna na popředí, ale nepřijímá události. Ve SwiftUI ji můžete pozorovat přes @Environment(\.scenePhase) a reagovat přes onChange. Při přechodu z .active do .inactive pozastavte časovače a animace. Při návratu do .active — obnovte. Při přechodu do .background — uložte stav.
Ne, pouze pro aplikace pracující s důvěrnými daty: bankovní, lékařské, firemní, messengery s privátními chaty. Pro hry a zábavní aplikace skrývání UI není vyžadováno. Pozastavení hry a zvuku při Inactive je však dobrá praxe, aby se předešlo překrývání zvuků se systémovými oznámeními. Apple doporučuje skrývání citlivých dat, ale nevyžaduje ho.
Shrnutí
Vyvineme mobilní aplikaci na klíč
IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.
Přečtěte si také