Интеграция Mapbox SDK в мобильное приложение
Mapbox — выбор, когда нужен полный контроль над визуальным стилем карты: кастомные цвета, шрифты, скрытие слоёв, добавление собственных источников данных поверх базовой карты. Mapbox GL / Mapbox Maps SDK v11 работают на векторных тайлах и рендерят карту нативно через Metal (iOS) или OpenGL ES / Vulkan (Android) — отсюда плавные повороты и зум без пикселизации.
Версионирование: v10 vs v11
Mapbox Maps SDK for 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) — отдельный от публичного access token. Оба нужны.
iOS (SPM):
// В Xcode: File → Add Package Dependencies
// URL: https://github.com/mapbox/mapbox-maps-ios
// Version: 11.x
Access token задаётся в 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.
Offline Maps
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 дня. Стоимость рассчитывается индивидуально.







