Розробка публікування постів з зображеннями в мобільній програмі
Публікування фото-поста виглядає простою функцією на перший погляд. У практиці вона включає вибір зображень, кадрування, стиснення, завантаження на сервер та створення запису в базі даних. Кожен крок можна реалізувати правильно або перетворити на джерело помилок.
Вибір та кадрування
PHPickerViewController (iOS 14+) — стандартний вибір. selectionLimit управляє кількістю фото: для постів зазвичай 1–10. PHPickerFilter.images фільтрує тільки фотографії, виключаючи відео та LivePhoto за замовчуванням.
Після вибору фото часто потрібне кадрування. На iOS — TOCropViewController (open-source) або кастомний UIScrollView з UIImageView всередині та pan/zoom жестами. Для збереження пропорцій при кадруванні: обчисліть область кропу CGRect у координатах оригінального зображення через CGAffineTransform — не у координатах UIView, інакше при зумі будуть зміщення.
На Android — uCrop (Yalantis) широко використовується у production: підтримує aspect ratio, ротацію, кастомний тулбар. Альтернатива — android-image-cropper.
Стиснення перед завантаженням
Стратегія: максимальна сторона 1440px для постів (на відміну від 1280px для чату — пости зазвичай переглядаються крупніше), якість JPEG 80%. Це дає файл 200–400 КБ з прийнятною візуальною якістю.
На iOS UIGraphicsImageRenderer з CGSize target та jpegData(compressionQuality: 0.80). Операція виконується у DispatchQueue.global(qos: .userInitiated). При мультивиборі (5+ фото) — послідовна обробка у фоні з прогресом на UI.
На Android — Bitmap.createScaledBitmap() з фільтрацією true для кращої якості масштабування, потім compress(JPEG, 80, stream). Обчисліть BitmapFactory.Options.inSampleSize заздалегідь за метаданими, щоб не завантажувати оригінал у повному розмірі у пам'ять.
Завантаження та синхронізація з постом
Зображення завантажуються до створення поста. Алгоритм:
- Завантажте всі фото паралельно (не послідовно) — кожне в окремий
URLSessionUploadTask. - Зберіть масив URL/ключів з відповідей сервера.
- Створіть пост з цим масивом через окремий API-запит.
Паралельне завантаження на iOS через withTaskGroup (Swift Concurrency) або DispatchGroup. На Android — coroutineScope { launch { ... } } для кожного файлу, awaitAll().
Покажіть прогрес сумарно: «завантажено X з N фото». Не блокуйте кнопку публікування жорстко — якщо одне фото з п'яти не завантажилося, покажіть повтор тільки для нього, інші вже готові.
Оптимістичний пост: створіть запис у локальній БД (Core Data, Room) одразу зі статусом uploading, покажіть у стрічці з індикатором. При успішному завантаженні оновіть статус на published. При невдачі — failed з кнопкою повтору. Користувач не чекає — він вже бачить свій пост.
Відображення у стрічці
Сітка фото в посту (кілька зображень) — UICollectionView з flow layout на iOS, LazyVerticalGrid (Compose) або RecyclerView з GridLayoutManager на Android. Перше фото крупне, інші — у сітці 2×N — класичний Instagram-паттерн.
Lazy loading через Kingfisher (iOS) або Coil (Android) з disk-кешуванням. Placeholder до завантаження — blur-hash з метаданих (якщо сервер його повертає) або solid color з домінантного кольору зображення.
Часові рамки
Вибір фото + стиснення + завантаження + відображення у посту — 1–3 дні. Кадрування + мультивибір + оптимістичний UI — ще 1–2 дні. Вартість розраховується індивідуально.







