Чому стандартний UIImageView не підходить для перегляду 360°-фотографій?
UIImageView відображає плоске зображення. Для сферичної проекції потрібен 3D-рендер: сфера з інвертованими нормалями (щоб дивитися зсередини), equirectangular-текстура на неї, камера в центрі, керування поворотом через gyroscope або touch.
Ми розробляємо кастомні рішення для перегляду 360°-фотографій під ключ. 360°-фото — це equirectangular проекція: сферична проекція, де 360° по горизонталі і 180° по вертикалі упаковані в прямокутник із співвідношенням сторін 2:1. На телефоні його потрібно «розгорнути» назад у сферу і дати користувачеві дивитися зсередини. Задача не в тому, щоб показати картинку — задача в тому, щоб сфера рендерилася без distortion-артефактів і скролінг не лагав навіть на 50 МПікс JPEG. Досвід нашої команди — понад 7 років у mobile-розробці, більше 30 реалізованих проектів з панорамами. Сертифіковані інженери гарантують стабільну роботу на широкому спектрі пристроїв. Економія бюджету порівняно з in-house розробкою може сягати до 50%. Зв'яжіться з нами — ми оцінимо ваш проект і запропонуємо оптимальне рішення.
Як реалізувати плавне керування переглядом?
Керування через гіроскоп — CMMotionManager → Euler angles → SCNNode.rotation. Через тач — UIPanGestureRecognizer з накопиченням кута повороту. Інерція при свайпі реалізується через SCNAction з easing, не через фізику — фізика SceneKit додає непередбачуване затухання.
let sceneView = SCNView(frame: view.bounds) let scene = SCNScene() // Сфера з інвертованими нормалями let sphere = SCNSphere(radius: 10.0) sphere.segmentCount = 96 // більше сегментів = менше distortion на полюсах let material = SCNMaterial() material.isDoubleSided = true material.diffuse.contents = UIImage(named: "pano.jpg") sphere.materials = [material] let sphereNode = SCNNode(geometry: sphere) sphereNode.scale = SCNVector3(-1, 1, 1) // інверсія нормалей scene.rootNode.addChildNode(sphereNode) let camera = SCNCamera() camera.fieldOfView = 90 let cameraNode = SCNNode() cameraNode.camera = camera scene.rootNode.addChildNode(cameraNode) sceneView.scene = scene Чому важливе тайлове завантаження для великих зображень?
JPEG 50 МПікс після декодування в RGB займає ~150 МБ в пам'яті. На iPhone SE 2nd gen з 3 ГБ RAM це вже третина доступної пам'яті — при фонових процесах отримуєте SIGKILL за memory pressure. Рішення — тайлове завантаження через CATiledLayer або downscale перед передачею в SceneKit. Це критично для стабільності і дозволяє уникнути дорогих збоїв.
// Прогресивне завантаження: спочатку прев'ю 2048px, потім повне розширення let thumbnail = image.preparingThumbnail(of: CGSize(width: 4096, height: 2048)) material.diffuse.contents = thumbnail // Асинхронно завантажуємо повне розширення Task.detached(priority: .background) { let full = await loadFullResImage(url: imageURL) await MainActor.run { material.diffuse.contents = full } } preparingThumbnail(of:) — асинхронний API (iOS 15+), декодує в background thread без блокування main run loop.
Google Photo Sphere XMP-метадані: як коректно обробляти
Panorama-камери та Google Street View пишуть в JPEG XMP-метадані з типом проекції та crop-інформацією (GPano:ProjectionType, GPano:CroppedAreaImageWidthPixels). Без їх читання неповна панорама (270° замість 360°) буде показана як повна — з розтягнутими краями. Читаємо через CGImageSourceCopyPropertiesAtIndex → XMP metadata → парсимо GPano:* поля, коригуємо UV-mapping текстури. Це 30–50 рядків коду, але без них третина реальних панорам виглядає неправильно.
Android: Filament vs OpenGL ES
На Android використовуємо Filament (Google's physically-based renderer) або Panorama360View через GLSurfaceView. Filament вимагає більше налаштувань, але дає коректний tone mapping для HDR-панорам. OpenGL ES 3.0 — більш легкий варіант, підходить для старих пристроїв. Наш досвід показує, що Filament в 2 рази швидше обробляє HDR-панорами, але для звичайних JPEG різниця непомітна.
Порівняння технологій для iOS та Android
| Платформа | Інструмент | Продуктивність | Складність | Рекомендація |
|---|---|---|---|---|
| iOS | SceneKit | Висока (нативна підтримка) | Низька | Швидкий прототип, не HDR |
| iOS | Metal | Максимальна | Висока | HDR, кастомні шейдери |
| Android | Filament | Висока (HDR, PBR) | Середня | HDR панорами |
| Android | OpenGL ES | Середня | Висока | Сумісність з застарілими пристроями |
Порівняння методів завантаження по пам'яті
| Метод | Споживання RAM (50 МПікс) | Час до першого кадру |
|---|---|---|
| Повне завантаження | ~150 МБ | 0.5–1 с (декодування) |
| Тайлове з прев'ю | ~12 МБ (прев'ю) | <0.1 с |
Що входить в реалізацію під ключ
- Проектування архітектури та вибір оптимального інструменту (SceneKit/Filament).
- Інтеграція керування через гіроскоп та touch з інерцією.
- Тайлове завантаження великих зображень з прогресивним відображенням.
- Парсинг XMP-метаданих та коригування UV-mapping.
- Тестування на 5+ реальних пристроях (включаючи iPhone SE, Pixel 6, Samsung Galaxy).
- Документація коду, інструкція з інтеграції, доступ до репозиторію.
- Гарантія стабільної роботи: 30 днів безкоштовної підтримки після здачі.
Терміни та вартість
Базовий переглядач (iOS, SceneKit, gyroscope + touch): 1–1.5 тижні. Повна реалізація з тайловим завантаженням, XMP-метаданими, iOS + Android: 3–4 тижні. Вартість розраховується індивідуально після аналізу ваших вимог. Замовте консультацію — ми підготуємо комерційну пропозицію протягом 1 робочого дня. Зв'яжіться з нами для оцінки вашого проекту.
Покрокова інтеграція 360°-переглядача
- Імпортуйте фреймворк SceneKit (iOS) або підключіть Filament (Android).
- Створіть 3D-сферу з радіусом 10 та інвертованими нормалями (scale = -1).
- Налаштуйте камеру в центр сцени з кутом огляду 90°.
- Завантажте текстурне зображення за допомогою прогресивного завантаження (прев'ю → повне).
- Реалізуйте керування: гіроскоп (CMMotionManager) та тач (UIPanGestureRecognizer з інерцією).
- Додайте парсинг XMP-метаданих для коригування UV-mapping неповних панорам.
- Протестуйте на реальних пристроях з різною роздільною здатністю екрана та об'ємом RAM.







