Публікація постів із зображеннями: стиснення, завантаження, UI
Ви коли-небудь стикалися з тим, що додаток вилітає при виборі фото з галереї? Або завантаження зображень триває вічність, а пост публікується без картинок? Ми — команда мобільних розробників з досвідом понад 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. Отримайте консультацію по вашому проєкту — зв'яжіться з нами.







