関連する 前回の記事で SwiftUI を使って iOS 上に Mapbox の地図を表示することができたので、次の ステップ として地図に マーカー (ピン) を立てて、特定の地点を指し示すようにしてみたいと思います。
Xcode: 13.2.1 iOS: 15.2 Swift: 5.5 mapbox-maps-ios: v10.2.0
Mapbox 公式 ドキュメント の説明
Mapbox 公式 ドキュメント には Example として、以下のように コペンハーゲン に マーカー を表示する サンプル が掲載されています。まずは、その サンプル の内容を確認していくことにします。
引用元: https://docs.mapbox.com/ios/maps/examples/point-annotation/
UIViewController
前回の記事同様、 公式 サイト で提供されているのは UIViewController のものですので、 SwiftUI で扱うためには UIViewControllerRepresentable を用いる必要があります。 考え方は前回の記事と同様ですので、詳細については前回の記事を参考にしてください。
MapBox MapView の生成
サンプル で紹介されている ViewController では最初に マップ の中心を指定し、 マップ を生成しています。
// Center the map camera over Copenhagen. let centerCoordinate = CLLocationCoordinate2D(latitude: 55.665957, longitude: 12.550343) let options = MapInitOptions(cameraOptions: CameraOptions(center: centerCoordinate, zoom: 8.0)) mapView = MapView(frame: view.bounds, mapInitOptions: options) mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] view.addSubview(mapView)
ただし、このままでは マップ を表示することができないため、以下のように修正していきます。 ”Your Access Token” には有効な アクセストークン を指定してください。
- myResourceOptions として アクセストークンをセット
- MapInitOptions に resourceOptions パラメータ を追加
// Set your access token let myResourceOptions = ResourceOptions(accessToken: "Your Access Token") // Center the map camera over Copenhagen. let centerCoordinate = CLLocationCoordinate2D(latitude: 55.665957, longitude: 12.550343) let options = MapInitOptions(resourceOptions: myResourceOptions, cameraOptions: CameraOptions(center: centerCoordinate, zoom: 8.0)) mapView = MapView(frame: view.bounds, mapInitOptions: options) mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] view.addSubview(mapView)
ここで、いくつか馴染みの薄そうな アイテム について補足していきます。
CLLocationCoordinate2D
CLLocationCoordinate2D は 緯度 と 経度 を パラメータ として生成する位置情報のオブジェクトです。今回の例では マップ の中心座標として利用しています。
MapInitOptions
ResourceOptions として accessToken を指定したり、 CameraOptions として 中心座標や縮尺などを指定した マップ 生成時の オプション 用 オブジェクト です。
cameraState
MapView 生成後 サンプル では続いて以下の コード が登場します。
// We want to display the annotation at the center of the map's current viewport let centerCoordinate = mapView.cameraState.center
中心座標を指定し マップ を生成した直後に再度中心座標を取得しているので、今回の実装においては実質的に意味はないため、 今回は コメントアウト してしまいます。
PointAnnotation, AnnotationOrchestrator
続いて サンプルコード では続いて以下の コード が登場します。
// Make a `PointAnnotationManager` which will be responsible for managing a // collection of `PointAnnotation`s. let pointAnnotationManager = mapView.annotations.makePointAnnotationManager() // Initialize a point annotation with a single coordinate // and configure it with a custom image (sourced from the asset catalogue) var customPointAnnotation = PointAnnotation(coordinate: centerCoordinate)
PointAnnotationManager, PointAnnotation という今回の トピック の肝になる オブジェクト を扱っています。 “PointAnnotationManager” という配列 を生成し、 後ほどその配列に登録する要素になる PointAnnotation を生成しています。
理解のため、それぞれ少し掘り下げてみます。
PointAnnotation
PointAnnotation とは Annotation プロトコル に準拠した構造体です。
PointAnnotation
https://docs.mapbox.com/ios/maps/api/10.2.0/Structs/PointAnnotation.html#/PointAnnotation
public struct PointAnnotation : Annotation
Annotation プロトコル の内容と共に PointAnnotation 構造体 の内容を確認してみます。
下表の通り、 PointAnnotation 構造体 は Annotation プロトコル に point プロパティ が追加されていることが分かります。
struct PointAnnotation | protocol Annotation |
id | id |
geometry | geometry |
point | |
userInfo | userInfo |
Annotation
https://docs.mapbox.com/ios/maps/api/10.2.0/Protocols/Annotation.html#/Annotation
public protocol Annotation
つまり、 geometry (地図情報) を表現する Annotation プロトコル に point (地図上の特定地点) の表現を追加したものが PointAnnotation ということになります。 本記事の トピック でもある マーカー ピン を地図上で指定するためには、この PointAnnotation 構造体の point に マーカー ピン を立てたい座標を指定すれば良いことが分かります。
このことが理解できると、 直前に実施している PointAnnotationManager の活用方法の理解が早くなるはずです。
PointAnnotationManager
PointAnnotationManager は端的にいうと PointAnnotation の配列です。 マップ 上に複数の マーカー ピン を立てる場面を想像すれば配列を活用する メリット み見えてくると思います。
AnnotationOrchestrator は PointAnnotationManager を含む Annotation を扱う プロパティ や メソッド を定義した クラス になります。 Annotation を扱う際に活用することになります。
AnnotationOrchestrator に関連する部分の理解が深まったところで、 再度 サンプルコード の流れに戻ってみます。
生成した PointAnnotation に image パラメータを追加
PointAnnotation の生成後 サンプルコード では続いて以下の コード が登場します。 ここでは先ほど生成した PointAnnotation オブジェクト に Image Convenience を 用いて マーカー 用の画像 イメージ を指定しています。
// Make the annotation show a red pin customPointAnnotation.image = .init(image: UIImage(named: "red_pin")!, name: "red_pin")
なお、ここで指定している “red_pin” は別途 Xcode の Assets に登録する必要があります。
画像を設定した PointAnnotation を PointAnnotationManager 配列として設定
最後に、画像を設定した PointAnnotation オブジェクト を 先ほど作成した PointAnnotationManager に設定しています。
// Add the annotation to the manager in order to render it on the map. pointAnnotationManager.annotations = [customPointAnnotation]
こうすることで、MapView内にこの画像設定済みの PointAnnotation が配置され、マップに描画されるようになるということです。
以上で マーカー ピン を表示する準備が整いましたので、 早速実行してみたいと思います。
iOS 上で Mapbox マップ に マーカー ピン を表示
実行すると以下のようにマーカーが指定した画像で指定した地点に表示されます。
まとめ
- MapBox の MapView に マーカー ピン を立てるためには立てたい地点を PointAnnotation 構造体で生成
- 生成した PointAnnotation 構造体を PointAnnotationManager 配列に登録
- マーカー 用の画像は別途 Assets に登録しておき Image Convenience で指定
関連記事
参照情報
docs.mapbox.com Add a marker to the map | Maps SDK | iOS | Mapbox
www.oreilly.com Head First Swift [Book]
docs.swift.org Protocols — The Swift Programming Language (Swift 5.5)
Mapbox マップ 表示に用いた コード
ContentView.swift
import SwiftUI struct ContentView: View { var body: some View { MapViewWrapper() } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } }
MapWithMarkerView.swift
import SwiftUI import MapboxMaps struct MapViewWrapper : UIViewControllerRepresentable { func makeUIViewController(context: Context) -> ViewController { return ViewController() } func updateUIViewController(_ uiViewController: ViewController, context: Context) { } } class ViewController: UIViewController { var mapView: MapView! override func viewDidLoad() { super.viewDidLoad() // Set your access token let myResourceOptions = ResourceOptions(accessToken: "Your Access Token") // Center the map camera over Copenhagen. let centerCoordinate = CLLocationCoordinate2D(latitude: 55.665957, longitude: 12.550343) let options = MapInitOptions(resourceOptions: myResourceOptions, cameraOptions: CameraOptions(center: centerCoordinate, zoom: 8.0)) mapView = MapView(frame: view.bounds, mapInitOptions: options) mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] view.addSubview(mapView) // Make a `PointAnnotationManager` which will be responsible for managing a // collection of `PointAnnotation`s. let pointAnnotationManager = mapView.annotations.makePointAnnotationManager() // Initialize a point annotation with a single coordinate // and configure it with a custom image (sourced from the asset catalogue) var customPointAnnotation = PointAnnotation(coordinate: centerCoordinate) // Make the annotation show a red pin customPointAnnotation.image = .init(image: UIImage(named: "red_pin")!, name: "red_pin") // Add the annotation to the manager in order to render it on the map. pointAnnotationManager.annotations = [customPointAnnotation] } }