[ SwiftUI ][ Mapbox ] iOS アプリ で Mapbox 地図に マーカー ピン を立てる

関連する 前回の記事で SwiftUI を使って iOS 上に Mapbox の地図を表示することができたので、次の ステップ として地図に マーカー (ピン) を立てて、特定の地点を指し示すようにしてみたいと思います。

Xcode: 13.2.1
iOS: 15.2
Swift: 5.5
mapbox-maps-ios: v10.2.0

Mapbox 公式 ドキュメント の説明

[ads]

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
public struct PointAnnotation : Annotation

https://docs.mapbox.com/ios/maps/api/10.2.0/Structs/PointAnnotation.html#/PointAnnotation

Annotation プロトコル の内容と共に PointAnnotation 構造体 の内容を確認してみます。
下表の通り、 PointAnnotation 構造体 は Annotation プロトコル に point プロパティ が追加されていることが分かります。

struct PointAnnotationprotocol Annotation
idid
geometrygeometry
point
userInfouserInfo
Property comparison PointAnnotation vs Annotation

Annotation
public protocol Annotation

https://docs.mapbox.com/ios/maps/api/10.2.0/Protocols/Annotation.html#/Annotation

つまり、 geometry (地図情報) を表現する Annotation プロトコル に point (地図上の特定地点) の表現を追加したものが PointAnnotation ということになります。 本記事の トピック でもある マーカー ピン を地図上で指定するためには、この PointAnnotation 構造体の point に マーカー ピン を立てたい座標を指定すれば良いことが分かります。

このことが理解できると、 直前に実施している PointAnnotationManager の活用方法の理解が早くなるはずです。

PointAnnotationManager

PointAnnotationManager は端的にいうと PointAnnotation の配列です。 マップ 上に複数の マーカー ピン を立てる場面を想像すれば配列を活用する メリット み見えてくると思います。

AnnotationOrchestrator

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 に登録する必要があります。

register “red_pin” as 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 マップ に マーカー ピン を表示

[ads]

実行すると以下のようにマーカーが指定した画像で指定した地点に表示されます。


まとめ

[ads]
  • 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 マップ 表示に用いた コード

[ads]

ContentView.swift

import SwiftUI

struct ContentView: View {
    var body: some View {
        MapViewWrapper()
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
[ads]

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]
    }
}