mapbox-ios-patterns について
Swift、SwiftUI、UIKitで地図アプリを開発するiOS開発者向けに、Mapbox Maps SDK v11の連携パターンをまとめたスキルです。地図の初期化、注釈、位置表示、独自データ、操作に加え、性能とトラブルシューティングも扱います。
主な機能
- 01SwiftUIとUIKitでの地図初期化
- 02マーカーと注釈の追加
- 03ユーザー位置表示とカメラ追従
- 04GeoJSON独自データの読み込み
- 05地図スタイルと操作の処理
- 06注釈の性能とライフサイクルの指針
活用シーン
- 01iOSアプリへのMapbox地図の組み込み
- 02場所を示す独自マーカーの追加
- 03地図上でのユーザー位置の表示
- 04地図上の要素タップやジェスチャーの処理
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ mapbox/ mapbox-agent-skills --skill mapbox-ios-patternsインストール後、Claude Code で/mapbox-ios-patternsを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
AGENTS.mdevals/evals.jsonreferences/annotations.mdcamera-styles.mdcustom-data.mdinteractions.mdlocation-tracking.md詳細
Mapbox iOS 統合パターン
Swift、SwiftUI、UIKit を使用して、iOS で Mapbox Maps SDK v11 を統合するための公式パターンです。
次の用途でこのスキルを使用します:
- Mapbox Maps SDK for iOS のインストールと設定
- 地図へのマーカーとアノテーションの追加
- ユーザーの位置の表示とカメラによる追跡
- カスタムデータ(GeoJSON)の地図への追加
- 地図のスタイル、カメラ、ユーザー操作の処理
- フィーチャーの操作とタップの処理
公式リソース:
インストールとセットアップ
要件
- iOS 14+
- Xcode 15+
- Swift 5.9+
- 無料の Mapbox アカウント
手順 1:アクセストークンを設定する
公開トークンを Info.plist に追加します:
<key>MBXAccessToken</key>
<string>pk.your_mapbox_token_here</string>
トークンの取得: mapbox.com にサインインします。
手順 2:Swift Package の依存関係を追加する
- File → Add Package Dependencies
- URL を入力:
https://github.com/mapbox/mapbox-maps-ios.git - バージョン:
11.0.0から「Up to Next Major」を選択します - 確認: MapboxCommon、MapboxCoreMaps、MapboxMaps、Turf の 4 つの依存関係が表示されることを確認します
代替方法: CocoaPods または直接ダウンロード(インストールガイド)
地図の初期化
SwiftUI パターン
基本的な地図:
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)
))
UIKit パターン
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)
}
}
マーカーを追加する
SDK には、地図上にポイントを配置する方法が 3 つあります。用途に合った、最もシンプルな方法を選んでください。
エージェント向けの注意: SwiftUI の Mapbox スケッチには、少なくとも 1 つのアノテーション(Marker、PointAnnotation、または MapViewAnnotation)が必要です。ピンのない Map { } をそのまま出荷しないでください。
どの API を使えばよいですか?
| API | 使用する状況 | プラットフォーム | 備考 |
|---|---|---|---|
Marker(Markers API) | デフォルトのピンが必要で、カスタム画像アセットがない場合 | SwiftUI のみ | 画像アセットは不要です。Experimental SPI のため、@_spi(Experimental) import MapboxMaps が必要です。100 個未満のマーカーに最適です。 |
PointAnnotation | カスタム画像があり、レイヤーレベルで配置したい場合 | SwiftUI + UIKit | シンボルレイヤーをベースにしているため、数百個のマーカーにも適切に対応できます。UIKit が描画できる任意の UIImage を受け付けます。 |
ビューアノテーション(ViewAnnotation / MapViewAnnotation) | 座標に固定した完全なネイティブビュー(カード、バッジ、アニメーションコンテンツ)を表示したい場合 | SwiftUI + UIKit | SwiftUI では MapViewAnnotation を使用し、UIKit では ViewAnnotation とともに mapView.viewAnnotations を使用します。各アノテーションは実際のビューであり、大規模な場合は PointAnnotation よりコストがかかります。 |
数百から数千のフィーチャーには、アノテーションではなくスタイルレイヤー(GeoJSONSource 上の SymbolLayer)を使用してください。
Markers API(シンプルなケースに推奨、SwiftUI)
import SwiftUI
@_spi(Experimental) import MapboxMaps
struct ContentView: View {
var body: some View {
Map {
Marker(coordinate: CLLocationCoordinate2D(latitude: 37.7749, longitude: -122.4194))
.color(.red)
.text("San Francisco")
}
}
}
コレクションから複数のマーカーを表示する場合:
Map {
ForEvery(locations, id: \.id) { location in
Marker(coordinate: location.coordinate)
.color(.red)
.text(location.name)
}
}
スケーリングに関する注意。
MarkerとPointAnnotationはそれぞれ、ピンごとに独自のビューまたはシンボルエントリーを作成します。マーカーが約100個までであれば問題ありません。より大きなデータセット(数百または数千のフィーチャー。オープンエンドなGeoJSONフィードでは一般的です)では、データをGeoJSONSourceに読み込み、代わりにSymbolLayerで描画してください。これにより、数千のフィーチャーに対応でき、クラスタリングも有効になります。
PointAnnotation(カスタム画像)
SwiftUI:
Map(viewport: $viewport) {
PointAnnotation(coordinate: CLLocationCoordinate2D(latitude: 37.7749, longitude: -122.4194))
.image(.init(image: UIImage(named: "marker")!, name: "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]
複数のマーカー:
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
ユーザーの位置を表示する
手順1:Info.plist に位置情報の使用許可を追加します:
<key>NSLocationWhenInUseUsageDescription</key>
<string>Show your location on the map</string>
手順2:使用許可をリクエストして位置情報を表示します:
import CoreLocation
// Request permissions
let locationManager = CLLocationManager()
locationManager.requestWhenInUseAuthorization()
// Show user location puck
mapView.location.options.puckType = .puck2D()
mapView.location.options.puckBearingEnabled = true
パフォーマンスに関するベストプラクティス
Annotation Manager を再利用する
// ❌ 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
トラブルシューティング
マップが表示されない
確認項目:
- ✅ Info.plist の
MBXAccessToken - ✅ トークンが有効であること(mapbox.com でテストしてください)
- ✅ MapboxMaps フレームワークがインポートされていること
- ✅ MapView がビュー階層に追加されていること
- ✅ 正しいフレーム/制約が設定されていること
スタイルが読み込まれない
mapView.mapboxMap.onStyleLoaded.observe { [weak self] _ in
print("Style loaded successfully")
// Add layers and sources here
}.store(in: &cancelables)
パフォーマンスの問題
.standardスタイルを使用します(推奨されており、最適化されています)- ビューポート内に表示するアノテーションの数を制限します
- Annotation Manager を再利用します
- スタイルの頻繁な再読み込みを避けます
- アノテーションの更新をバッチ処理します
参考ファイル
より詳しいパターンが必要なタスクでは、以下のリファレンスを読み込んでください:
references/annotations.md— Circle、Polyline、Polygon のアノテーションreferences/location-tracking.md— カメラでユーザーを追跡する方法 + 現在地の取得references/custom-data.md— GeoJSON:ライン、ポリゴン、ポイント、更新/削除references/camera-styles.md— カメラ制御 + マップスタイルreferences/interactions.md— フィーチャーセットとのインタラクション、カスタムレイヤーのタップ、長押し、ジェスチャー