Реализация мини-плеера (Mini Player) в мобильном приложении
Мини-плеер — полоска с названием трека, кнопками play/pause и прогресс-баром, которая остаётся видимой при навигации между экранами. Выглядит просто, но технически упирается в архитектуру навигации: плеер должен жить вне конкретного экрана и не пересоздаваться при переходах.
Архитектурное решение
Мини-плеер — не экран, а persistented overlay поверх всего приложения. Способ размещения зависит от навигационной системы.
iOS (UIKit). Добавляем мини-плеер в UITabBarController или UIWindow — он живёт на уровне контейнера, не конкретного ViewController. Ограничение: UITabBar занимает нижнюю часть, мини-плеер размещаем прямо над ним, сдвигая additionalSafeAreaInsets.bottom у всех VC в табе.
// В кастомном UITabBarController
override func viewDidLoad() {
super.viewDidLoad()
miniPlayerView = MiniPlayerView()
view.addSubview(miniPlayerView)
NSLayoutConstraint.activate([
miniPlayerView.bottomAnchor.constraint(equalTo: tabBar.topAnchor),
miniPlayerView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
miniPlayerView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
miniPlayerView.heightAnchor.constraint(equalToConstant: 64)
])
// Сдвигаем safe area для дочерних VC
additionalSafeAreaInsets.bottom = 64
}
iOS (SwiftUI). ZStack в корневом ContentView с TabView внизу и мини-плеером поверх через .overlay(alignment: .bottom). Состояние плеера — @EnvironmentObject или @ObservedObject синглтон.
Android (Jetpack Compose). Мини-плеер в корневом Scaffold как bottomBar, или через Box поверх NavHost. Состояние через ViewModel в NavGraph-уровне — переживает навигацию между экранами.
React Native. Компонент мини-плеера в корневом App.tsx поверх навигатора. position: 'absolute', bottom: tabBarHeight + жест свайпа вверх для разворачивания.
Expand/Collapse анимация
Мини-плеер должен разворачиваться в полноэкранный плеер по тапу или свайпу вверх.
iOS. UIPanGestureRecognizer + UIViewPropertyAnimator с interruptibleAnimator. При промежуточных состояниях — fractionComplete анимируется пропорционально смещению пальца. Apple Music делает именно так: полноэкранный плеер — это sheet с detents, мини-плеер — кастомный presentationController.
Android Compose. BottomSheetScaffold или ModalBottomSheet с кастомными sheetPeekHeight и sheetContent. При peekHeight — мини-плеер, при развёрнутом состоянии — полный.
Синхронизация состояния
Прогресс-бар, название трека, обложка — всё это должно обновляться в реальном времени вне зависимости от того, на каком экране пользователь. Единый источник правды — PlayerViewModel / AudioPlayerManager как синглтон с @Published (iOS) или StateFlow (Android). Мини-плеер подписывается на него, не хранит локального состояния.
Жесты и доступность
Свайп вниз на развёрнутом плеере — закрыть/свернуть в мини. Реализуем через UIPanGestureRecognizer (iOS) с пороговым значением translation.y > 100 для подтверждения действия. При неполном свайпе — UIViewPropertyAnimator возвращает в исходное состояние.
На Android Compose — swipeable (из accompanist) или anchoredDraggable (Compose 1.6+) с якорями COLLAPSED / EXPANDED. При скорости жеста > 1000 dp/s — немедленный переход без анимации.
VoiceOver/TalkBack: мини-плеер должен иметь accessibilityLabel с текущим треком и accessibilityHint «Нажмите для открытия полного плеера». Кнопка паузы в мини-плеере — отдельный accessibilityElement.
Сроки
Мини-плеер с expand-анимацией на одной платформе — 2–3 дня. Обе платформы с жестовым управлением — 3–4 дня.







