Разработка публикации постов с изображениями в мобильном приложении
Вы когда-нибудь сталкивались с тем, что приложение вылетает при выборе фото из галереи? Или загрузка изображений занимает вечность, а пост публикуется без картинок? Мы — команда мобильных разработчиков с опытом более 5 лет — реализуем публикацию постов с изображениями так, чтобы пользователь не ждал, а разработчик не ловил баги. В этой статье разберём ключевые этапы: выбор фото, кадрирование, сжатие, загрузку и отображение. Расскажем, как избежать типичных ошибок и сделать механизм под ключ за 2–5 дней.
Как организовать выбор и кадрирование изображений?
Выбор фото — первый шаг, где многие проекты спотыкаются. На iOS используем PHPickerViewController (iOS 14+). Устанавливаем selectionLimit — обычно 1–10 для поста. PHPickerFilter.images исключает видео. На Android — стандартный ActivityResultContracts.PickMultipleVisualMedia или кастомный выбор. Однако важно учесть разрешения: на iOS требуется NSPhotoLibraryUsageDescription и обработка ATT (AppTrackingTransparency) для iOS 14.5+. На Android достаточно READ_EXTERNAL_STORAGE или READ_MEDIA_IMAGES для API 33+.
Кадрирование часто требуется для единообразного вида постов. На iOS применяем TOCropViewController — open-source библиотеку, проверенную в сотнях проектов. Она поддерживает aspect ratio, ротацию, кастомный toolbar. Если нужно встроенное решение — кастомный UIScrollView с UIImageView, где преобразование области кропа в координаты изображения делаем через CGAffineTransform. На Android используем uCrop — гибкий и стабильный инструмент. Для сложных случаев (как кадрирование с поворотом на 45°) uCrop предоставляет встроенный механизм.
Сжатие перед загрузкой: почему это критично?
Сжатие напрямую влияет на скорость и трафик. Мы используем единый стандарт: максимальная сторона 1440px (для постов — больше, чем для чата), качество JPEG 80%. Итоговый файл — 200–400 КБ, визуальное качество — отличное. На iOS работа выполняется в DispatchQueue.global(qos: .userInitiated) с помощью UIGraphicsImageRenderer и jpegData(compressionQuality: 0.80). При мультивыборе (5+ фото) — последовательная обработка в фоне с отображением прогресса. На Android — Bitmap.createScaledBitmap() с фильтрацией true и compress(JPEG, 80, stream). Заранее вычисляем inSampleSize через BitmapFactory.Options, чтобы не загружать оригинал в память. Это предотвращает OutOfMemoryError на устройствах с 2 ГБ RAM.
Загрузка и оптимистичный post
Изображения загружаем до создания поста. Алгоритм:
- Загружаем все фото параллельно — каждое в отдельный
URLSessionUploadTask(iOS) илиlaunchв корутинах (Android). - Собираем массив URL/ключей из ответов.
- Создаём пост с этим массивом через API.
Прогресс отображаем суммарно: "загружено X из N фото". Если одно фото из пяти не загрузилось — retry только для него, остальные уже на сервере.
Оптимистичный post: создаём запись в локальной БД (Core Data, Room) немедленно со статусом uploading, показываем в ленте с индикатором. При успехе — статус published, при ошибке — failed с кнопкой повтора. Пользователь не ждёт.
Почему параллельная загрузка быстрее?
Сравнение последовательной и параллельной загрузки для 5 фото:
| Параметр | Последовательная загрузка | Параллельная загрузка |
|---|---|---|
| Время для 5 фото | ~10 сек (по 2 сек) | ~2 сек (минимальное) |
| Сложность отлова ошибок | Легче | Сложнее, но мы реализовали |
| UX | Блокирует UI | Прогресс отображается |
При параллельной загрузке также важно управлять количеством одновременных задач. На iOS используем URLSession с HTTPMaximumConnectionsPerHost (по умолчанию 6), на Android — Dispatchers.IO с ограничением через semaphore.
Отображение в ленте: алгоритмы и патенты
Сетка фото — UICollectionView с flow layout (iOS) или LazyVerticalGrid (Compose). Первое фото крупное, остальные — сетка 2×N (Instagram-паттерн). Lazy loading через Kingfisher (iOS) или Coil (Android) с disk-кэшированием. Placeholder — blur-hash из метаданных или solid color из dominant color. Для изображений с прозрачностью (PNG) используем библиотеку SDWebImage (iOS) или Glide (Android), так как Kingfisher/Coil оптимизированы для JPEG.
Что входит в работу
Реализуем модуль под ключ:
- Интеграция выбора фото из галереи (с учётом разрешений ATT на iOS).
- Кадрирование с заданными пропорциями (1:1, 4:5, 16:9).
- Сжатие с настраиваемыми параметрами (максимальная сторона, качество, формат).
- Параллельная загрузка на сервер с прогрессом.
- Оптимистичное сохранение в локальную БД.
- Отображение в ленте с ленивой загрузкой.
- Документация API, помощь в модерации App Store.
- Гарантия качества и поддержка после деплоя.
Сроки и стоимость
Базовая интеграция — 1–3 дня. Кадрирование, мультивыбор, оптимистичный UI — ещё 1–2 дня. Стоимость рассчитывается индивидуально — напишите нам для оценки вашего проекта. Опыт нашей команды — 5+ лет, 20+ успешных проектов с медиаконтентом. Мы гарантируем стабильную работу и соблюдение гайдлайнов App Store и Google Play. Получите консультацию по вашему проекту — свяжитесь с нами.







