Разработка Reels (короткие видео) в мобильном приложении
Reels — вертикальный видеофид с автовоспроизведением при скролле. Технически это один из самых сложных UI-паттернов в мобильной разработке: плавный paging-скролл, предзагрузка видео, плеер с синхронизированным звуком, бесконечная лента с пагинацией. Каждый из этих компонентов можно реализовать так, чтобы он работал без задержек — или так, чтобы он убил UX.
Вертикальный paging-скролл
Основа Reels — UICollectionView с UICollectionViewCompositionalLayout + UICollectionLayoutSectionOrthogonalScrollingBehavior.paging (iOS) или RecyclerView с PagerSnapHelper (Android). Каждый элемент занимает весь экран (bounds.size).
На iOS пагинационный скролл через UICollectionView с isPagingEnabled = true — работает, но не даёт контроля над деceleration. Лучше: кастомный UICollectionViewFlowLayout с переопределённым targetContentOffset(forProposedContentOffset:withScrollingVelocity:), который snap'ает к ближайшему видео с нужной анимацией.
Velocity-based переключение: быстрый свайп переключает на следующий Reel независимо от того, насколько далеко проскролили. sqrt(velocity.x² + velocity.y²) > threshold → snap к следующему элементу.
Плеер и переключение между видео
Критическое решение: один shared AVPlayer или по плееру на ячейку?
Один shared AVPlayer с переназначением AVPlayerItem при скролле — стандартный подход для Reels. При scrollViewDidEndDecelerating забираем плеер из предыдущей ячейки и назначаем текущей. AVPlayerLayer.player = avPlayer — мгновенно. Плеер не пересоздаётся — переключение без пауз.
На Android — один ExoPlayer на всю активность/фрагмент. PlayerView.player = exoPlayer переназначает. Используем MediaItem с preload: exoPlayer.addMediaItem(nextMediaItem) для следующего видео — ExoPlayer начинает буферизацию до фактического переключения.
Предзагрузка: держим в памяти AVPlayerItem для текущего, следующего и предыдущего видео. Создание AVPlayerItem из URL занимает время — делаем это при начале скролла к соседнему элементу, не при завершении.
func collectionView(_ collectionView: UICollectionView,
willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
// Предзагружаем AVPlayerItem для этого индекса
let item = AVPlayerItem(url: videoURLs[indexPath.row])
playerItemCache[indexPath.row] = item
}
Запись коротких видео
Камера для записи Reels — кастомный AVCaptureSession. AVCaptureVideoPreviewLayer отображает live preview на весь экран. Запись через AVCaptureMovieFileOutput с ограничением: maxRecordedDuration = CMTime(seconds: 60, preferredTimescale: 600).
Мультисегментная запись (несколько коротких клипов в один Reel) — на каждую запись создаём отдельный AVAsset, в конце объединяем через AVMutableComposition. AVMutableCompositionTrack.insertTimeRange() вставляет сегменты последовательно.
Фильтры в реальном времени — Core Image с CIFilter применяем к CMSampleBuffer в методе делегата captureOutput(_:didOutput:from:). Рендерим через CIContext с Metal (CIContext(mtlDevice: MTLCreateSystemDefaultDevice()!)). OpenGL для этой задачи в 2025 году — устаревший подход с худшей производительностью.
Звук, текст, эффекты
Аудиотрек — своя музыка из медиатеки через MPMediaPickerController (iOS) или ACTION_PICK с MediaStore.Audio (Android), или треки из каталога приложения. Аудио накладываем на видео через AVMutableComposition с двумя треками: видео + аудио.
Текст поверх видео — не UILabel поверх UIImageView, а запекаем в видео через AVVideoCompositionCoreAnimationTool + CATextLayer. Это даёт корректное отображение текста при экспорте и публикации.
Производительность ленты
UICollectionView.prefetchDataSource на iOS — запрашиваем метаданные следующих 3 видео при скролле. RecyclerView.setRecycledViewPool() на Android — общий пул вью для переиспользования, снижает число inflate-операций.
Память: храним декодированные AVPlayerItem максимум для 5 соседних видео. При прокрутке дальше — освобождаем AVPlayerItem.asset явно. Без этого при просмотре 20+ Reels память вырастает до 300–500 МБ и iOS отправляет memory warning.
| Компонент | Сложность | Примерное время |
|---|---|---|
| Вертикальный paging + shared плеер | Высокая | 2–3 дня |
| Предзагрузка + кэш | Средняя | 1–2 дня |
| Запись + мультисегменты | Высокая | 3–4 дня |
| Фильтры real-time (Core Image) | Высокая | 2–3 дня |
| Текст + аудио в экспорте | Средняя | 2 дня |
Сроки
Просмотр ленты с автовоспроизведением, предзагрузкой и пагинацией — 1–2 недели. Полный цикл (запись, редактирование, фильтры, публикация, лента) — 3–6 недель в зависимости от набора функций. Стоимость рассчитывается после анализа требований.







