mapbox-ios-patterns by mapbox/mapbox-agent-skills
npx skills add https://github.com/mapbox/mapbox-agent-skills --skill mapbox-ios-patterns在 iOS 上使用 Swift、SwiftUI 和 UIKit 集成 Mapbox Maps SDK v11 的官方模式。
在以下情况下使用此技能:
官方资源:
将您的公共令牌添加到 Info.plist:
<key>MBXAccessToken</key>
<string>pk.your_mapbox_token_here</string>
获取您的令牌: 登录
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
https://github.com/mapbox/mapbox-maps-ios.git11.0.0 开始选择 "Up to Next Major"替代方案: CocoaPods 或直接下载(安装指南)
基础地图:
import SwiftUI
import MapboxMaps
struct ContentView: View {
@State private var viewport: Viewport = .camera(
center: CLLocationCoordinate2D(latitude: 37.7749, longitude: -122.4194),
zoom: 12
)
var body: some View {
Map(viewport: $viewport)
.mapStyle(.standard)
}
}
带装饰控件:
Map(viewport: $viewport)
.mapStyle(.standard)
.ornamentOptions(OrnamentOptions(
scaleBar: .init(visibility: .visible),
compass: .init(visibility: .adaptive),
logo: .init(position: .bottomLeading)
))
import UIKit
import MapboxMaps
class MapViewController: UIViewController {
private var mapView: MapView!
override func viewDidLoad() {
super.viewDidLoad()
let options = MapInitOptions(
cameraOptions: CameraOptions(
center: CLLocationCoordinate2D(latitude: 37.7749, longitude: -122.4194),
zoom: 12
)
)
mapView = MapView(frame: view.bounds, mapInitOptions: options)
mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
view.addSubview(mapView)
mapView.mapboxMap.loadStyle(.standard)
}
}
点标注是地图上标记位置最常用的方式。
SwiftUI:
Map(viewport: $viewport) {
PointAnnotation(coordinate: CLLocationCoordinate2D(latitude: 37.7749, longitude: -122.4194))
.iconImage("custom-marker")
}
UIKit:
// 创建标注管理器(创建一次,重复用于更新)
var pointAnnotationManager = mapView.annotations.makePointAnnotationManager()
// 创建标记
var annotation = PointAnnotation(coordinate: CLLocationCoordinate2D(latitude: 37.7749, longitude: -122.4194))
annotation.image = .init(image: UIImage(named: "marker")!, name: "marker")
annotation.iconAnchor = .bottom
// 添加到地图
pointAnnotationManager.annotations = [annotation]
多个标记:
let locations = [
CLLocationCoordinate2D(latitude: 37.7749, longitude: -122.4194),
CLLocationCoordinate2D(latitude: 37.7849, longitude: -122.4094),
CLLocationCoordinate2D(latitude: 37.7649, longitude: -122.4294)
]
let annotations = locations.map { coordinate in
var annotation = PointAnnotation(coordinate: coordinate)
annotation.image = .init(image: UIImage(named: "marker")!, name: "marker")
return annotation
}
pointAnnotationManager.annotations = annotations
var circleAnnotationManager = mapView.annotations.makeCircleAnnotationManager()
var circle = CircleAnnotation(coordinate: coordinate)
circle.circleRadius = 10
circle.circleColor = StyleColor(.red)
circleAnnotationManager.annotations = [circle]
var polylineAnnotationManager = mapView.annotations.makePolylineAnnotationManager()
let coordinates = [coord1, coord2, coord3]
var polyline = PolylineAnnotation(lineCoordinates: coordinates)
polyline.lineColor = StyleColor(.blue)
polyline.lineWidth = 4
polylineAnnotationManager.annotations = [polyline]
var polygonAnnotationManager = mapView.annotations.makePolygonAnnotationManager()
let coordinates = [coord1, coord2, coord3, coord1] // 闭合多边形
var polygon = PolygonAnnotation(polygon: .init(outerRing: .init(coordinates)))
polygon.fillColor = StyleColor(.blue.withAlphaComponent(0.5))
polygon.fillOutlineColor = StyleColor(.blue)
polygonAnnotationManager.annotations = [polygon]
步骤 1:向 Info.plist 添加位置权限:
<key>NSLocationWhenInUseUsageDescription</key>
<string>在地图上显示您的位置</string>
步骤 2:请求权限并显示位置:
import CoreLocation
// 请求权限
let locationManager = CLLocationManager()
locationManager.requestWhenInUseAuthorization()
// 显示用户位置指示器
mapView.location.options.puckType = .puck2D()
mapView.location.options.puckBearingEnabled = true
使相机在用户移动时跟随其位置:
import Combine
class MapViewController: UIViewController {
private var mapView: MapView!
private var cancelables = Set<AnyCancellable>()
override func viewDidLoad() {
super.viewDidLoad()
setupMap()
setupLocationTracking()
}
func setupLocationTracking() {
// 请求权限
let locationManager = CLLocationManager()
locationManager.requestWhenInUseAuthorization()
// 显示用户位置
mapView.location.options.puckType = .puck2D()
mapView.location.options.puckBearingEnabled = true
// 使用相机跟随用户位置
mapView.location.onLocationChange.observe { [weak self] locations in
guard let self = self, let location = locations.last else { return }
self.mapView.camera.ease(to: CameraOptions(
center: location.coordinate,
zoom: 15,
bearing: location.course >= 0 ? location.course : nil,
pitch: 45
), duration: 1.0)
}.store(in: &cancelables)
}
}
if let location = mapView.location.latestLocation {
let coordinate = location.coordinate
print("用户位于:\(coordinate.latitude), \(coordinate.longitude)")
// 将相机移动到用户位置
mapView.camera.ease(to: CameraOptions(
center: coordinate,
zoom: 14
), duration: 1.0)
}
使用 GeoJSON 源和图层将您自己的数据添加到地图。
// 为线创建坐标
let routeCoordinates = [
CLLocationCoordinate2D(latitude: 37.7749, longitude: -122.4194),
CLLocationCoordinate2D(latitude: 37.7849, longitude: -122.4094),
CLLocationCoordinate2D(latitude: 37.7949, longitude: -122.3994)
]
// 创建 GeoJSON 源
var source = GeoJSONSource(id: "route-source")
source.data = .geometry(.lineString(LineString(routeCoordinates)))
try? mapView.mapboxMap.addSource(source)
// 创建线图层
var layer = LineLayer(id: "route-layer", source: "route-source")
layer.lineColor = .constant(StyleColor(.blue))
layer.lineWidth = .constant(4)
layer.lineCap = .constant(.round)
layer.lineJoin = .constant(.round)
try? mapView.mapboxMap.addLayer(layer)
let polygonCoordinates = [coord1, coord2, coord3, coord1] // 闭合多边形
var source = GeoJSONSource(id: "area-source")
source.data = .geometry(.polygon(Polygon([polygonCoordinates])))
try? mapView.mapboxMap.addSource(source)
var fillLayer = FillLayer(id: "area-fill", source: "area-source")
fillLayer.fillColor = .constant(StyleColor(.blue.withAlphaComponent(0.3)))
fillLayer.fillOutlineColor = .constant(StyleColor(.blue))
try? mapView.mapboxMap.addLayer(fillLayer)
let geojsonString = """
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": {"type": "Point", "coordinates": [-122.4194, 37.7749]},
"properties": {"name": "Location 1"}
},
{
"type": "Feature",
"geometry": {"type": "Point", "coordinates": [-122.4094, 37.7849]},
"properties": {"name": "Location 2"}
}
]
}
"""
var source = GeoJSONSource(id: "points-source")
source.data = .string(geojsonString)
try? mapView.mapboxMap.addSource(source)
var symbolLayer = SymbolLayer(id: "points-layer", source: "points-source")
symbolLayer.iconImage = .constant(.name("marker"))
symbolLayer.textField = .constant(.expression(Exp(.get) { "name" }))
symbolLayer.textOffset = .constant([0, 1.5])
try? mapView.mapboxMap.addLayer(symbolLayer)
try? mapView.mapboxMap.updateLayer(
withId: "route-layer",
type: LineLayer.self
) { layer in
layer.lineColor = .constant(StyleColor(.red))
layer.lineWidth = .constant(6)
}
try? mapView.mapboxMap.removeLayer(withId: "route-layer")
try? mapView.mapboxMap.removeSource(withId: "route-source")
// SwiftUI - 更新视口状态
viewport = .camera(
center: CLLocationCoordinate2D(latitude: 40.7128, longitude: -74.0060),
zoom: 14,
bearing: 90,
pitch: 60
)
// UIKit - 立即执行
mapView.mapboxMap.setCamera(to: CameraOptions(
center: CLLocationCoordinate2D(latitude: 40.7128, longitude: -74.0060),
zoom: 14,
bearing: 90,
pitch: 60
))
// 飞行动画(弧形轨迹)
mapView.camera.fly(to: CameraOptions(
center: destination,
zoom: 15
), duration: 2.0)
// 缓动动画(平滑)
mapView.camera.ease(to: CameraOptions(
center: destination,
zoom: 15
), duration: 1.0)
let coordinates = [coord1, coord2, coord3]
let camera = mapView.mapboxMap.camera(for: coordinates,
padding: UIEdgeInsets(top: 50, left: 50, bottom: 50, right: 50),
bearing: 0,
pitch: 0)
mapView.camera.ease(to: camera, duration: 1.0)
// SwiftUI
Map(viewport: $viewport)
.mapStyle(.standard) // Mapbox Standard(推荐)
.mapStyle(.streets) // Mapbox Streets
.mapStyle(.outdoors) // Mapbox Outdoors
.mapStyle(.light) // Mapbox Light
.mapStyle(.dark) // Mapbox Dark
.mapStyle(.standardSatellite) // 卫星影像
// UIKit
mapView.mapboxMap.loadStyle(.standard)
mapView.mapboxMap.loadStyle(.streets)
mapView.mapboxMap.loadStyle(.dark)
// SwiftUI
Map(viewport: $viewport)
.mapStyle(MapStyle(uri: StyleURI(url: customStyleURL)!))
// UIKit
mapView.mapboxMap.loadStyle(StyleURI(url: customStyleURL)!)
来自 Mapbox Studio 的样式:
let styleURL = URL(string: "mapbox://styles/username/style-id")!
现代的 Interactions API 允许处理地图要素的点击事件,并提供类型化的要素访问。适用于 Standard Style 预定义的要素集,如 POI、建筑物和地名标签。
SwiftUI 模式:
import SwiftUI
import MapboxMaps
struct MapView: View {
@State private var viewport: Viewport = .camera(
center: CLLocationCoordinate2D(latitude: 37.7749, longitude: -122.4194),
zoom: 12
)
@State private var selectedBuildings = [StandardBuildingsFeature]()
var body: some View {
Map(viewport: $viewport) {
// 点击 POI 要素
TapInteraction(.standardPoi) { poi, context in
print("点击了 POI:\(poi.name ?? "Unknown")")
return true // 停止传播
}
// 点击建筑物并收集选中的建筑物
TapInteraction(.standardBuildings) { building, context in
print("点击了建筑物")
selectedBuildings.append(building)
return true
}
// 对选中的建筑物应用要素状态(高亮显示)
ForEvery(selectedBuildings, id: \.id) { building in
FeatureState(building, .init(select: true))
}
}
.mapStyle(.standard)
}
}
UIKit 模式:
import MapboxMaps
import Combine
class MapViewController: UIViewController {
private var mapView: MapView!
private var cancelables = Set<AnyCancellable>()
override func viewDidLoad() {
super.viewDidLoad()
setupMap()
setupInteractions()
}
func setupInteractions() {
// 点击 POI 要素
let poiToken = mapView.mapboxMap.addInteraction(
TapInteraction(.standardPoi) { [weak self] poi, context in
print("点击了 POI:\(poi.name ?? "Unknown")")
return true
}
)
// 点击建筑物
let buildingToken = mapView.mapboxMap.addInteraction(
TapInteraction(.standardBuildings) { [weak self] building, context in
print("点击了建筑物")
// 使用要素状态高亮显示建筑物
self?.mapView.mapboxMap.setFeatureState(
building,
state: ["select": true]
)
return true
}
)
// 存储令牌以保持交互活动状态
// 完成后取消令牌:poiToken.cancel()
}
}
let token = mapView.mapboxMap.addInteraction(
TapInteraction(.layer("custom-layer-id")) { feature, context in
if let properties = feature.properties {
print("要素属性:\(properties)")
}
return true
}
)
let token = mapView.mapboxMap.addInteraction(
LongPressInteraction(.standardPoi) { poi, context in
print("长按了 POI:\(poi.name ?? "Unknown")")
return true
}
)
// UIKit
mapView.gestures.onMapTap.observe { [weak self] context in
let coordinate = context.coordinate
print("在地图上点击了:\(coordinate.latitude), \(coordinate.longitude)")
}.store(in: &cancelables)
// 禁用特定手势
mapView.gestures.options.pitchEnabled = false
mapView.gestures.options.rotateEnabled = false
// 配置缩放限制
mapView.mapboxMap.setCamera(to: CameraOptions(
zoom: 12,
minZoom: 10,
maxZoom: 16
))
// ❌ 不要重复创建新的管理器
func updateMarkers() {
let manager = mapView.annotations.makePointAnnotationManager()
manager.annotations = markers
}
// ✅ 创建一次,重复使用
let pointAnnotationManager: PointAnnotationManager
init() {
pointAnnotationManager = mapView.annotations.makePointAnnotationManager()
}
func updateMarkers() {
pointAnnotationManager.annotations = markers
}
// ✅ 一次性全部更新
pointAnnotationManager.annotations = newAnnotations
// ❌ 不要逐个更新
for annotation in newAnnotations {
pointAnnotationManager.annotations.append(annotation)
}
// 在闭包中使用 weak self
mapView.gestures.onMapTap.observe { [weak self] context in
self?.handleTap(context.coordinate)
}.store(in: &cancelables)
// 在 deinit 时清理
deinit {
cancelables.forEach { $0.cancel() }
}
// ✅ Standard 样式经过优化,推荐使用
.mapStyle(.standard)
// 仅在特定用例需要时使用其他样式
.mapStyle(.standardSatellite) // 卫星影像
检查:
Info.plist 中的 MBXAccessTokenmapView.mapboxMap.onStyleLoaded.observe { [weak self] _ in
print("样式加载成功")
// 在此处添加图层和源
}.store(in: &cancelables)
.standard 样式(推荐且经过优化)每周安装数
185
代码库
GitHub 星标数
35
首次出现
2026年2月1日
安全审计
安装于
gemini-cli166
opencode166
codex165
github-copilot159
cursor158
amp150
Official patterns for integrating Mapbox Maps SDK v11 on iOS with Swift, SwiftUI, and UIKit.
Use this skill when:
Official Resources:
Add your public token to Info.plist:
<key>MBXAccessToken</key>
<string>pk.your_mapbox_token_here</string>
Get your token: Sign in at mapbox.com
https://github.com/mapbox/mapbox-maps-ios.git11.0.0Alternative: CocoaPods or direct download (install guide)
Basic map:
import SwiftUI
import MapboxMaps
struct ContentView: View {
@State private var viewport: Viewport = .camera(
center: CLLocationCoordinate2D(latitude: 37.7749, longitude: -122.4194),
zoom: 12
)
var body: some View {
Map(viewport: $viewport)
.mapStyle(.standard)
}
}
With ornaments:
Map(viewport: $viewport)
.mapStyle(.standard)
.ornamentOptions(OrnamentOptions(
scaleBar: .init(visibility: .visible),
compass: .init(visibility: .adaptive),
logo: .init(position: .bottomLeading)
))
import UIKit
import MapboxMaps
class MapViewController: UIViewController {
private var mapView: MapView!
override func viewDidLoad() {
super.viewDidLoad()
let options = MapInitOptions(
cameraOptions: CameraOptions(
center: CLLocationCoordinate2D(latitude: 37.7749, longitude: -122.4194),
zoom: 12
)
)
mapView = MapView(frame: view.bounds, mapInitOptions: options)
mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
view.addSubview(mapView)
mapView.mapboxMap.loadStyle(.standard)
}
}
Point annotations are the most common way to mark locations on the map.
SwiftUI:
Map(viewport: $viewport) {
PointAnnotation(coordinate: CLLocationCoordinate2D(latitude: 37.7749, longitude: -122.4194))
.iconImage("custom-marker")
}
UIKit:
// Create annotation manager (once, reuse for updates)
var pointAnnotationManager = mapView.annotations.makePointAnnotationManager()
// Create marker
var annotation = PointAnnotation(coordinate: CLLocationCoordinate2D(latitude: 37.7749, longitude: -122.4194))
annotation.image = .init(image: UIImage(named: "marker")!, name: "marker")
annotation.iconAnchor = .bottom
// Add to map
pointAnnotationManager.annotations = [annotation]
Multiple markers:
let locations = [
CLLocationCoordinate2D(latitude: 37.7749, longitude: -122.4194),
CLLocationCoordinate2D(latitude: 37.7849, longitude: -122.4094),
CLLocationCoordinate2D(latitude: 37.7649, longitude: -122.4294)
]
let annotations = locations.map { coordinate in
var annotation = PointAnnotation(coordinate: coordinate)
annotation.image = .init(image: UIImage(named: "marker")!, name: "marker")
return annotation
}
pointAnnotationManager.annotations = annotations
var circleAnnotationManager = mapView.annotations.makeCircleAnnotationManager()
var circle = CircleAnnotation(coordinate: coordinate)
circle.circleRadius = 10
circle.circleColor = StyleColor(.red)
circleAnnotationManager.annotations = [circle]
var polylineAnnotationManager = mapView.annotations.makePolylineAnnotationManager()
let coordinates = [coord1, coord2, coord3]
var polyline = PolylineAnnotation(lineCoordinates: coordinates)
polyline.lineColor = StyleColor(.blue)
polyline.lineWidth = 4
polylineAnnotationManager.annotations = [polyline]
var polygonAnnotationManager = mapView.annotations.makePolygonAnnotationManager()
let coordinates = [coord1, coord2, coord3, coord1] // Close the polygon
var polygon = PolygonAnnotation(polygon: .init(outerRing: .init(coordinates)))
polygon.fillColor = StyleColor(.blue.withAlphaComponent(0.5))
polygon.fillOutlineColor = StyleColor(.blue)
polygonAnnotationManager.annotations = [polygon]
Step 1: Add location permission to Info.plist:
<key>NSLocationWhenInUseUsageDescription</key>
<string>Show your location on the map</string>
Step 2: Request permissions and show location:
import CoreLocation
// Request permissions
let locationManager = CLLocationManager()
locationManager.requestWhenInUseAuthorization()
// Show user location puck
mapView.location.options.puckType = .puck2D()
mapView.location.options.puckBearingEnabled = true
To make the camera follow the user's location as they move:
import Combine
class MapViewController: UIViewController {
private var mapView: MapView!
private var cancelables = Set<AnyCancellable>()
override func viewDidLoad() {
super.viewDidLoad()
setupMap()
setupLocationTracking()
}
func setupLocationTracking() {
// Request permissions
let locationManager = CLLocationManager()
locationManager.requestWhenInUseAuthorization()
// Show user location
mapView.location.options.puckType = .puck2D()
mapView.location.options.puckBearingEnabled = true
// Follow user location with camera
mapView.location.onLocationChange.observe { [weak self] locations in
guard let self = self, let location = locations.last else { return }
self.mapView.camera.ease(to: CameraOptions(
center: location.coordinate,
zoom: 15,
bearing: location.course >= 0 ? location.course : nil,
pitch: 45
), duration: 1.0)
}.store(in: &cancelables)
}
}
if let location = mapView.location.latestLocation {
let coordinate = location.coordinate
print("User at: \(coordinate.latitude), \(coordinate.longitude)")
// Move camera to user location
mapView.camera.ease(to: CameraOptions(
center: coordinate,
zoom: 14
), duration: 1.0)
}
Add your own data to the map using GeoJSON sources and layers.
// Create coordinates for the line
let routeCoordinates = [
CLLocationCoordinate2D(latitude: 37.7749, longitude: -122.4194),
CLLocationCoordinate2D(latitude: 37.7849, longitude: -122.4094),
CLLocationCoordinate2D(latitude: 37.7949, longitude: -122.3994)
]
// Create GeoJSON source
var source = GeoJSONSource(id: "route-source")
source.data = .geometry(.lineString(LineString(routeCoordinates)))
try? mapView.mapboxMap.addSource(source)
// Create line layer
var layer = LineLayer(id: "route-layer", source: "route-source")
layer.lineColor = .constant(StyleColor(.blue))
layer.lineWidth = .constant(4)
layer.lineCap = .constant(.round)
layer.lineJoin = .constant(.round)
try? mapView.mapboxMap.addLayer(layer)
let polygonCoordinates = [coord1, coord2, coord3, coord1] // Close the polygon
var source = GeoJSONSource(id: "area-source")
source.data = .geometry(.polygon(Polygon([polygonCoordinates])))
try? mapView.mapboxMap.addSource(source)
var fillLayer = FillLayer(id: "area-fill", source: "area-source")
fillLayer.fillColor = .constant(StyleColor(.blue.withAlphaComponent(0.3)))
fillLayer.fillOutlineColor = .constant(StyleColor(.blue))
try? mapView.mapboxMap.addLayer(fillLayer)
let geojsonString = """
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": {"type": "Point", "coordinates": [-122.4194, 37.7749]},
"properties": {"name": "Location 1"}
},
{
"type": "Feature",
"geometry": {"type": "Point", "coordinates": [-122.4094, 37.7849]},
"properties": {"name": "Location 2"}
}
]
}
"""
var source = GeoJSONSource(id: "points-source")
source.data = .string(geojsonString)
try? mapView.mapboxMap.addSource(source)
var symbolLayer = SymbolLayer(id: "points-layer", source: "points-source")
symbolLayer.iconImage = .constant(.name("marker"))
symbolLayer.textField = .constant(.expression(Exp(.get) { "name" }))
symbolLayer.textOffset = .constant([0, 1.5])
try? mapView.mapboxMap.addLayer(symbolLayer)
try? mapView.mapboxMap.updateLayer(
withId: "route-layer",
type: LineLayer.self
) { layer in
layer.lineColor = .constant(StyleColor(.red))
layer.lineWidth = .constant(6)
}
try? mapView.mapboxMap.removeLayer(withId: "route-layer")
try? mapView.mapboxMap.removeSource(withId: "route-source")
// SwiftUI - Update viewport state
viewport = .camera(
center: CLLocationCoordinate2D(latitude: 40.7128, longitude: -74.0060),
zoom: 14,
bearing: 90,
pitch: 60
)
// UIKit - Immediate
mapView.mapboxMap.setCamera(to: CameraOptions(
center: CLLocationCoordinate2D(latitude: 40.7128, longitude: -74.0060),
zoom: 14,
bearing: 90,
pitch: 60
))
// Fly animation (dramatic arc)
mapView.camera.fly(to: CameraOptions(
center: destination,
zoom: 15
), duration: 2.0)
// Ease animation (smooth)
mapView.camera.ease(to: CameraOptions(
center: destination,
zoom: 15
), duration: 1.0)
let coordinates = [coord1, coord2, coord3]
let camera = mapView.mapboxMap.camera(for: coordinates,
padding: UIEdgeInsets(top: 50, left: 50, bottom: 50, right: 50),
bearing: 0,
pitch: 0)
mapView.camera.ease(to: camera, duration: 1.0)
// SwiftUI
Map(viewport: $viewport)
.mapStyle(.standard) // Mapbox Standard (recommended)
.mapStyle(.streets) // Mapbox Streets
.mapStyle(.outdoors) // Mapbox Outdoors
.mapStyle(.light) // Mapbox Light
.mapStyle(.dark) // Mapbox Dark
.mapStyle(.standardSatellite) // Satellite imagery
// UIKit
mapView.mapboxMap.loadStyle(.standard)
mapView.mapboxMap.loadStyle(.streets)
mapView.mapboxMap.loadStyle(.dark)
// SwiftUI
Map(viewport: $viewport)
.mapStyle(MapStyle(uri: StyleURI(url: customStyleURL)!))
// UIKit
mapView.mapboxMap.loadStyle(StyleURI(url: customStyleURL)!)
Style from Mapbox Studio:
let styleURL = URL(string: "mapbox://styles/username/style-id")!
The modern Interactions API allows handling taps on map features with typed feature access. Works with Standard Style predefined featuresets like POIs, buildings, and place labels.
SwiftUI Pattern:
import SwiftUI
import MapboxMaps
struct MapView: View {
@State private var viewport: Viewport = .camera(
center: CLLocationCoordinate2D(latitude: 37.7749, longitude: -122.4194),
zoom: 12
)
@State private var selectedBuildings = [StandardBuildingsFeature]()
var body: some View {
Map(viewport: $viewport) {
// Tap on POI features
TapInteraction(.standardPoi) { poi, context in
print("Tapped POI: \(poi.name ?? "Unknown")")
return true // Stop propagation
}
// Tap on buildings and collect selected buildings
TapInteraction(.standardBuildings) { building, context in
print("Tapped building")
selectedBuildings.append(building)
return true
}
// Apply feature state to selected buildings (highlighting)
ForEvery(selectedBuildings, id: \.id) { building in
FeatureState(building, .init(select: true))
}
}
.mapStyle(.standard)
}
}
UIKit Pattern:
import MapboxMaps
import Combine
class MapViewController: UIViewController {
private var mapView: MapView!
private var cancelables = Set<AnyCancellable>()
override func viewDidLoad() {
super.viewDidLoad()
setupMap()
setupInteractions()
}
func setupInteractions() {
// Tap on POI features
let poiToken = mapView.mapboxMap.addInteraction(
TapInteraction(.standardPoi) { [weak self] poi, context in
print("Tapped POI: \(poi.name ?? "Unknown")")
return true
}
)
// Tap on buildings
let buildingToken = mapView.mapboxMap.addInteraction(
TapInteraction(.standardBuildings) { [weak self] building, context in
print("Tapped building")
// Highlight the building using feature state
self?.mapView.mapboxMap.setFeatureState(
building,
state: ["select": true]
)
return true
}
)
// Store tokens to keep interactions active
// Cancel tokens when done: poiToken.cancel()
}
}
let token = mapView.mapboxMap.addInteraction(
TapInteraction(.layer("custom-layer-id")) { feature, context in
if let properties = feature.properties {
print("Feature properties: \(properties)")
}
return true
}
)
let token = mapView.mapboxMap.addInteraction(
LongPressInteraction(.standardPoi) { poi, context in
print("Long pressed POI: \(poi.name ?? "Unknown")")
return true
}
)
// UIKit
mapView.gestures.onMapTap.observe { [weak self] context in
let coordinate = context.coordinate
print("Tapped map at: \(coordinate.latitude), \(coordinate.longitude)")
}.store(in: &cancelables)
// Disable specific gestures
mapView.gestures.options.pitchEnabled = false
mapView.gestures.options.rotateEnabled = false
// Configure zoom limits
mapView.mapboxMap.setCamera(to: CameraOptions(
zoom: 12,
minZoom: 10,
maxZoom: 16
))
// ❌ Don't create new managers repeatedly
func updateMarkers() {
let manager = mapView.annotations.makePointAnnotationManager()
manager.annotations = markers
}
// ✅ Create once, reuse
let pointAnnotationManager: PointAnnotationManager
init() {
pointAnnotationManager = mapView.annotations.makePointAnnotationManager()
}
func updateMarkers() {
pointAnnotationManager.annotations = markers
}
// ✅ Update all at once
pointAnnotationManager.annotations = newAnnotations
// ❌ Don't update one by one
for annotation in newAnnotations {
pointAnnotationManager.annotations.append(annotation)
}
// Use weak self in closures
mapView.gestures.onMapTap.observe { [weak self] context in
self?.handleTap(context.coordinate)
}.store(in: &cancelables)
// Clean up on deinit
deinit {
cancelables.forEach { $0.cancel() }
}
// ✅ Standard style is optimized and recommended
.mapStyle(.standard)
// Use other styles only when needed for specific use cases
.mapStyle(.standardSatellite) // Satellite imagery
Check:
MBXAccessToken in Info.plistmapView.mapboxMap.onStyleLoaded.observe { [weak self] _ in
print("Style loaded successfully")
// Add layers and sources here
}.store(in: &cancelables)
.standard style (recommended and optimized)Weekly Installs
185
Repository
GitHub Stars
35
First Seen
Feb 1, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
gemini-cli166
opencode166
codex165
github-copilot159
cursor158
amp150