Picture-in-Picture (PiP) — tryb odtwarzania wideo w pływającym oknie nad innymi aplikacjami, pozwalający użytkownikowi kontynuować oglądanie treści przy minimalizacji aplikacji lub przełączaniu się między programami. Okno PiP automatycznie pozycjonuje się w rogu ekranu i może być przesuwane przez użytkownika. Według Apple AVPictureInPictureController documentation (2026), tryb PiP jest obsługiwany na iOS od wersji 14 i na Android od wersji 8.0.
Najważniejsze
Picture-in-Picture (PiP) — to tryb wyświetlania wideo w małym pływającym oknie, które pozostaje nad wszystkimi innymi oknami i aplikacjami. Użytkownik może przesuwać okno PiP po ekranie, zmieniać jego rozmiar (na niektórych platformach) i kontynuować oglądanie treści podczas pracy w innych aplikacjach.
Koncepcja PiP pochodzi z telewizji: już w latach 90. telewizory umożliwiały wyświetlanie drugiego kanału w rogu ekranu. W urządzeniach mobilnych PiP po raz pierwszy pojawił się na iPadzie w iOS 9 (2015) dla wideo w Safari, a pełnoprawny systemowy PiP dla aplikacji stał się dostępny w iOS 14 (2020). Na Android wsparcie PiP pojawiło się wcześniej — w wersji 8.0 Oreo (2017), ale tylko dla wideo, a od Androida 12 — dla wszystkich typów treści.
PiP różni się od odtwarzania w tle tym, że wideo nadal wyświetla się na ekranie, a nie tylko odtwarza się w strumieniu audio. Odtwarzanie w tle (background audio) jest dostępne na obu platformach, ale PiP daje użytkownikowi wizualną kontrolę nad treścią: widzi klatki, może zatrzymać, przewinąć lub zamknąć okno. Jest to szczególnie ważne w przypadku lekcji wideo, streamów i rozmów wideo, gdzie treść wizualna jest równie ważna co audio.
Architektonicznie PiP jest realizowany przez systemowy menedżer okien, który tworzy osobne okno o niższym priorytecie wyświetlania. Aplikacja deleguje wyjście wideo do usługi systemowej, która kontynuuje renderowanie wideo nawet po przejściu aplikacji w tło lub po jej minimalizacji.
Proces rozpoczyna się, gdy użytkownik minimalizuje aplikację z aktywnym wideo lub naciska przycisk PiP (na iOS) lub system automatycznie przełącza Activity w tryb PiP (na Android). Systemowy menedżer okien przechwytuje strumień wideo i tworzy pływające okno o stałych proporcjach. Rozmiar okna zależy od proporcji oryginalnego wideo i ograniczeń platformy: na iOS okno PiP zajmuje około 1/6–1/4 szerokości ekranu, na Android — co najmniej 108 dp szerokości i 240 dp wysokości dla urządzeń mobilnych.
Gdy okno PiP jest aktywne, aplikacja może znajdować się w jednym z trzech stanów: w tle (zminimalizowana), w stanie aktywnym (użytkownik wrócił do aplikacji) lub w stanie oczekiwania (system wstrzymał PiP z powodu braku zasobów). Przy przejściu w PiP aplikacja powinna wstrzymać niepotrzebne operacje UI (animacje, renderowanie interfejsu) i zwolnić pamięć, ponieważ zasoby systemowe w trybie wielozadaniowym są dystrybuowane bardziej restrykcyjnie. iOS automatycznie wysyła do aplikacji powiadomienie AVPictureInPictureControllerWillStartNotification, a Android — callback onPictureInPictureModeChanged.
Okno PiP ma istotne ograniczenia: nie można na nim wyświetlać standardowych elementów sterowania UI (przycisk pauzy, suwak postępu) — tylko minimalny systemowy overlay z podstawowymi elementami: play/pause, zamknij, rozwiń do pełnego ekranu. Systemowy UI PiP na iOS zawiera przycisk pauzy i zamknięcia, a na Android — te same elementy plus dodatkowy przycisk ustawień. Interakcja z treścią wewnątrz PiP (przewijanie, wybór napisów) jest niemożliwa — wymaga rozwinięcia aplikacji na pełny ekran.
Na iOS PiP jest zaimplementowany przez framework AVKit i klasę AVPictureInPictureController. To API jest dostępne na iOS 14+ dla iPhone i iPad, ale z różnymi wymaganiami: na iPad PiP działa przez AVPlayerLayer, na iPhone — tylko przez AVPlayerViewController.
Do działania PiP na iOS należy spełnić kilka warunków: aplikacja musi używać AVPlayer do odtwarzania wideo, sesja audio musi być skonfigurowana na kategorię .playback lub .playAndRecord, a aplikacja musi posiadać entitlements dla dźwięku w tle (UIBackgroundModes = audio). Bez tych ustawień PiP nie uruchomi się — system odrzuci żądanie utworzenia sesji PiP, ponieważ nie będzie mógł zagwarantować poprawnego odtwarzania po przejściu w tło.
Na iOS okno PiP pojawia się automatycznie przy minimalizacji aplikacji, jeśli wideo jest aktywnie odtwarzane i użytkownik nie wyłączył tej funkcji w ustawieniach. Użytkownik może także ręcznie zminimalizować wideo do PiP za pomocą przycisku w AVPlayerViewController. Rozmiar okna PiP na iOS jest stały i określany przez system — deweloper nie może go zmienić. Proporcje okna PiP odpowiadają proporcjom oryginalnego wideo, ale maksymalny rozmiar jest ograniczony do 1/4 szerokości ekranu na iPhone i 1/3 na iPad.
Główne ograniczenia PiP na iOS: brak możliwości własnego UI w oknie PiP, ograniczenie do jednego strumienia PiP jednocześnie i wymóg aktywnego AVPlayer do działania PiP. Multi-PiP — jednoczesne odtwarzanie wielu okien PiP — nie jest obsługiwane na iOS. Przy próbie uruchomienia drugiego PiP pierwszy automatycznie się zamyka. To ograniczenie jest sprzętowe: procesor wideo nie może jednocześnie obsługiwać dwóch niezależnych kanałów PiP ze względu na ograniczenia DMA i pamięci wideo.
Kolejne ważne ograniczenie — czas odtwarzania w tle. Jeśli użytkownik nie wchodzi w interakcję z oknem PiP, system może wstrzymać odtwarzanie po pewnym czasie w celu oszczędzania energii. Automatyczna pauza PiP na iOS następuje po 10–15 minutach braku aktywności, jeśli aplikacja nie zaimplementowała mechanizmu keep-alive przez background task. W przypadku rozmów wideo i streamów zaleca się używanie PushKit i certyfikatów VoIP, które omijają to ograniczenie.
Na Android PiP jest zaimplementowany jako wbudowany tryb Activity, który aktywuje się przez metodę enterPictureInPictureMode. Od Android 8.0 (API 26) każda Activity może przejść w tryb PiP, a od Android 12 (API 31) pojawiło się wsparcie PiP dla SurfaceView i TextureView bez konieczności używania MediaCodec.
Do obsługi PiP w manifeście Android należy dodać atrybut android:supportsPictureInPicture dla Activity w sekcji
W Android okno PiP domyślnie nie ma elementów sterowania. Deweloper może dodać niestandardowe akcje przez RemoteAction w metodzie setPictureInPictureParams. Dostępne są do 3 akcji (np.: pauza/odtwórz, przewiń wstecz/do przodu, zamknij). Każda akcja wyświetla się w systemowym overlay PiP jako ikona. W przeciwieństwie do iOS, gdzie wszystkie elementy UI są ściśle ustalone, Android daje więcej elastyczności w zakresie podstawowych kontrolek.
PiP na Android ma różne możliwości w zależności od wersji systemu. Na Android 8.0–8.1 PiP jest dostępny tylko dla wideo odtwarzanego przez MediaPlayer lub MediaCodec z SurfaceView. Od Android 9 można używać PictureInPictureArgs.Builder do konfiguracji proporcji okna PiP. Android 12 dodał obsługę PiP dla niestandardowych SurfaceView i TextureView, a także ulepszone zarządzanie przejściami między pełnym ekranem a trybem PiP. Android 13+ umożliwia wyświetlanie okna PiP nawet przy zablokowanym ekranie, jeśli aplikacja ma odpowiednie uprawnienia.
| Wersja Android | Możliwości PiP | API |
|---|---|---|
| 8.0–8.1 | Podstawowy PiP dla MediaPlayer/MediaCodec | 26–27 |
| 9–11 | Konfiguracja proporcji, niestandardowe akcje | 28–30 |
| 12 | Obsługa SurfaceView/TextureView w PiP | 31 |
| 13+ | PiP na ekranie blokady, ulepszone animacje | 33+ |
Kluczowa różnica Android PiP od iOS — możliwość multi-PiP. Na Android od wersji 12 system może wyświetlać kilka okien PiP jednocześnie, jeśli aplikacje to obsługują i wydajność urządzenia na to pozwala. W praktyce jednak multi-PiP jest ograniczony możliwościami SoC: większość urządzeń obsługuje tylko jedno okno PiP ze względu na ograniczenia sprzętowe dekodera, ponieważ każde okno PiP wymaga własnego strumienia wideo i oddzielnej sesji dekodowania.
Rozważmy praktyczną implementację PiP na obu platformach mobilnych z uwzględnieniem najnowszych zmian API.
import AVKit
import AVFoundation
class VideoPlayerViewController: UIViewController {
var player: AVPlayer!
var pipController: AVPictureInPictureController?
override func viewDidLoad() {
super.viewDidLoad()
let playerLayer = AVPlayerLayer(player: player)
playerLayer.videoGravity = .resizeAspect
view.layer.addSublayer(playerLayer)
guard AVPictureInPictureController.isPictureInPictureSupported()
else { return }
pipController = AVPictureInPictureController(playerLayer: playerLayer)
pipController?.delegate = self
}
@IBAction func startPiPTapped() {
pipController?.startPictureInPicture()
}
}
extension VideoPlayerViewController: AVPictureInPictureControllerDelegate {
func pictureInPictureControllerWillStart(
_ pictureInPictureController: AVPictureInPictureController
) {
// Ukryj elementy UI, zwolnij pamięć
}
func pictureInPictureControllerDidStop(
_ pictureInPictureController: AVPictureInPictureController
) {
// Przywróć UI, wznowić renderowanie
}
}
W przykładzie AVPictureInPictureController jest inicjalizowany z playerLayer po sprawdzeniu isPictureInPictureSupported (PiP nie jest obsługiwany na iPhone SE 1. generacji i niektórych iPadach bez wystarczającej pamięci). Delegat powiadamia aplikację o rozpoczęciu i zakończeniu PiP — w tych callbackach należy ukryć i przywrócić elementy UI, ponieważ w trybie PiP interfejs aplikacji nie jest widoczny. Przy przejściu w PiP zaleca się zatrzymanie wszystkich animacji, ukrycie kontrolek odtwarzacza i zwolnienie nieużywanej pamięci, aby zapobiec wymuszonemu wyładowaniu aplikacji przez system.
class PipVideoActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setupVideoPlayer()
}
private fun enterPipMode() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val aspectRatio = Rational(16, 9)
val pipParams = PictureInPictureParams.Builder()
.setAspectRatio(aspectRatio)
.setAutoEnterEnabled(true)
.build()
enterPictureInPictureMode(pipParams)
}
}
override fun onPictureInPictureModeChanged(
isInPictureInPictureMode: Boolean,
newConfig: Configuration
) {
if (isInPictureInPictureMode) {
// Ukryj UI, skup się tylko na mediach
binding.controlsGroup.visibility = View.GONE
} else {
// Przywróć UI
binding.controlsGroup.visibility = View.VISIBLE
}
}
}
Przykład w Kotlin używa PictureInPictureParams.Builder do konfiguracji PiP. Metoda setAspectRatio ustawia proporcje okna PiP (16:9 dla typowego wideo). setAutoEnterEnabled(true) włącza automatyczne przejście w PiP przy minimalizacji aplikacji. Callback onPictureInPictureModeChanged jest wywoływany przy wejściu i wyjściu z PiP — w nim należy ukrywać lub pokazywać elementy UI. Dla wideo na SurfaceView wymagane jest dodatkowe obsłużenie konfiguracji poprzez dodanie android:configChanges="screenSize|smallestScreenSize" w manifeście, aby zapobiec odtworzeniu Activity przy przejściu w tryb PiP.
PiP to potężne narzędzie do poprawy doświadczenia użytkownika w aplikacjach, gdzie treść pozostaje istotna nawet przy przełączaniu się na inne zadania. Jednak wdrożenie PiP powinno być uzasadnione i nie rozpraszać użytkownika.
Rozmowy wideo i konferencje — jeden z głównych scenariuszy PiP. W Zoom, FaceTime, Google Meet PiP pozwala widzieć rozmówcę podczas pracy w innych aplikacjach: czytania notatek, przeglądania prezentacji lub sprawdzania poczty. PiP dla rozmów wideo wymaga obsługi kamery w tle i prawidłowej konfiguracji sesji audio do kontynuacji przechwytywania dźwięku w tle. Na iOS używa się do tego AVSampleBufferDisplayLayer zamiast AVPlayerLayer, ponieważ rozmowy wideo nie używają AVPlayer.
Serwisy streamingowe (YouTube, Netflix, Twitch) aktywnie używają PiP do kontynuacji oglądania podczas szukania nowych treści. YouTube Premium oferuje PiP jako płatną funkcję, Netflix również ogranicza PiP do określonych planów taryfowych ze względu na licencyjne ograniczenia treści. Do implementacji PiP w aplikacji streamingowej wymagana jest integracja z systemem DRM (FairPlay, Widevine), który obsługuje bezpieczny pipeline w trybie PiP.
PiP nie nadaje się do aplikacji z interaktywną treścią wideo, gdzie wymagana jest interakcja użytkownika: platformy edukacyjne z testami w odtwarzaczu, streamy gier z czatem, aplikacje zakupowe z linkami do produktów w wideo. W tych przypadkach okno PiP jest zbyt małe do wyświetlania dodatkowych informacji, a interaktywne elementy w PiP nie są obsługiwane przez system. Zaleca się używanie PiP tylko do pasywnego oglądania, gdy interakcja z treścią nie jest wymagana.
Dla aplikacji muzycznych i podcastów PiP jest zbędny — wystarczy dźwięk w tle bez okna wizualnego. PiP zużywa dodatkowe zasoby GPU do renderowania wideo w pływającym oknie, co skraca czas pracy na baterii. Jeśli treść jest audialna (muzyka, podcasty, audiobooki) — używaj odtwarzania w tle bez PiP. Jeśli wizualna — wdrażaj PiP dla poprawy doświadczenia użytkownika.
Często zadawane pytania
PiP na iOS wymaga iPhone 6s+, iOS 14+ i regionu z obsługą (USA, Kanada, Australia, UE, Rosja i inne). Dla aplikacji należy skonfigurować audio session na kategorię .playback i dodać UIBackgroundModes = audio. Sprawdź także ustawienia: Ustawienia > Ogólne > Obraz w obrazie.
Na iOS rozmiar okna PiP jest całkowicie określany przez system i nie jest konfigurowany przez dewelopera. Na Android można ustawić tylko proporcje przez setAspectRatio w PictureInPictureParams.Builder, ale dokładny rozmiar okna określa system. Użytkownik może zmienić rozmiar okna PiP na Android 12+ gestem pinch-to-zoom.
Tak, PiP działa z treścią chronioną DRM (FairPlay na iOS, Widevine L1 na Android) pod warunkiem, że sesja DRM obsługuje bezpieczny pipeline w trybie PiP. Widevine L3 może nie obsługiwać PiP, ponieważ nie gwarantuje bezpieczeństwa zdekodowanej treści w pływającym oknie. Sprawdzaj kompatybilność DRM z PiP na etapie testowania.
Na iOS — tylko jedno okno PiP. Na Android 12+ teoretycznie obsługiwany jest multi-PiP, ale w praktyce większość urządzeń jest ograniczona do jednego okna ze względu na ograniczenia sprzętowe. Urządzenia z najwyższej półki (Samsung Galaxy S24, Pixel 8) mogą obsługiwać 2 okna PiP, ale z obniżoną wydajnością.
Tak, obsługa lifecycle jest krytyczna. Na iOS przy przejściu w PiP aplikacja otrzymuje powiadomienie willStart, gdzie należy ukryć UI i zwolnić pamięć. Na Android onPictureInPictureModeChanged jest wywoływany przy wejściu/wyjściu z PiP. Przy nieprawidłowej obsłudze lifecycle system może wyładować aplikację z pamięci, przerywając odtwarzanie.
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ż