Інтеграція Mapbox SDK у мобільний додаток
Mapbox — вибір, коли потрібен повний контроль над візуальним стилем карти: кастомні кольори, шрифти, приховування шарів, додавання власних джерел даних поверх базової карти. Mapbox GL / Mapbox Maps SDK v11 працюють із векторними плитками і відображають карту нативно через Metal (iOS) або OpenGL ES / Vulkan (Android) — звідси плавне обертання та зум без піксельності.
Версіонування: v10 vs v11
Mapbox Maps SDK для Android v11 (2023+) і iOS v11 — це фактично новий SDK з іншим API порівняно з v9/v10. Основні зміни: MapboxMap замість MapboxMapOptions, ViewAnnotation замість кастомних MarkerView, PointAnnotationManager замість SymbolManager. Якщо знайдете приклади з addMarker() — це v9, не застосовується.
Підключення
Android:
// settings.gradle
dependencyResolutionManagement {
repositories {
maven {
url = uri("https://api.mapbox.com/downloads/v2/releases/maven")
authentication { create<BasicAuthentication>("basic") }
credentials {
username = "mapbox"
password = providers.gradleProperty("MAPBOX_DOWNLOADS_TOKEN").get()
}
}
}
}
// build.gradle (app)
implementation("com.mapbox.maps:android:11.7.0")
Токен для завантаження SDK (MAPBOX_DOWNLOADS_TOKEN) — окремий від публічного токену доступу. Обидва необхідні.
iOS (SPM):
// У Xcode: File → Add Package Dependencies
// URL: https://github.com/mapbox/mapbox-maps-ios
// Version: 11.x
Токен доступу задається в Info.plist ключем MBXAccessToken або програмно:
MapboxOptions.accessToken = "pk.eyJ1IjoiLi4uIn0..."
Базова карта з кастомним стилем
import MapboxMaps
class MapViewController: UIViewController {
var mapView: MapView!
override func viewDidLoad() {
super.viewDidLoad()
let cameraOptions = CameraOptions(
center: CLLocationCoordinate2D(latitude: 55.7558, longitude: 37.6173),
zoom: 12
)
let initOptions = MapInitOptions(
cameraOptions: cameraOptions,
styleURI: StyleURI(rawValue: "mapbox://styles/your-username/your-style-id")
// або StyleURI.streets / .dark / .satellite
)
mapView = MapView(frame: view.bounds, mapInitOptions: initOptions)
mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
view.addSubview(mapView)
}
}
Стилі створюються в Mapbox Studio — веб-редакторі з повним контролем над кожним шаром. Зміни в Studio публікуються миттєво без оновлення додатку.
PointAnnotationManager: маркери в v11
mapView.mapboxMap.onMapLoaded.observeNext { [weak self] _ in
guard let self else { return }
var annotationManager = self.mapView.annotations.makePointAnnotationManager()
var annotations = [PointAnnotation]()
let points: [(Double, Double, String)] = [
(55.7558, 37.6173, "Центр"),
(55.7518, 37.6105, "Арбат")
]
for (lat, lng, title) in points {
var annotation = PointAnnotation(
coordinate: CLLocationCoordinate2D(latitude: lat, longitude: lng)
)
annotation.image = .init(image: UIImage(named: "custom-pin")!, name: "custom-pin")
annotation.iconSize = 1.0
annotation.textField = title
annotation.textOffset = [0, 2]
annotations.append(annotation)
}
annotationManager.annotations = annotations
annotationManager.annotationInteractionDelegate = self
}
Android: додавання GeoJSON-шару
Mapbox дозволяє додати власний GeoJSON як джерело даних і відобразити його через стиль — це потужніше за маркери для великої кількості точок:
mapView.mapboxMap.loadStyle(Style.MAPBOX_STREETS) { style ->
// Додаємо джерело
style.addSource(
GeoJsonSource.Builder("locations-source")
.data("""
{
"type": "FeatureCollection",
"features": [
{ "type": "Feature",
"geometry": { "type": "Point", "coordinates": [37.6173, 55.7558] },
"properties": { "name": "Точка А" }
}
]
}
""".trimIndent())
.build()
)
// Додаємо шар символів
style.addLayer(
SymbolLayer("locations-layer", "locations-source").apply {
iconImage("custom-icon")
iconSize(1.0)
textField(get("name"))
textOffset(listOf(0.0, 1.5))
}
)
}
Для 10 000+ точок це єдиний продуктивний підхід — PointAnnotationManager з тисячами об'єктів починає проседати по FPS.
Офлайн-карти
val offlineManager = OfflineManager()
val tileStore = TileStore.create()
val tilesetDescriptor = offlineManager.createTilesetDescriptor(
TilesetDescriptorOptions.Builder()
.styleURI(Style.MAPBOX_STREETS)
.minZoom(10)
.maxZoom(16)
.build()
)
val tileRegionLoadOptions = TileRegionLoadOptions.Builder()
.geometry(Point.fromLngLat(37.6173, 55.7558)) // або Polygon для області
.descriptors(listOf(tilesetDescriptor))
.build()
tileStore.loadTileRegion("moscow-center", tileRegionLoadOptions, { progress ->
Log.d("Mapbox", "Downloaded: ${progress.completedResourceCount}/${progress.requiredResourceCount}")
}, { result ->
result.fold({ region -> Log.d("Mapbox", "Done: ${region.id}") }, { error -> })
})
Строки
1–3 дні. Базова карта з кастомним стилем — 1 день. GeoJSON-шари, офлайн, кластеризація — 2–3 дні. Вартість розраховується індивідуально.







