【Mapkit】ClusterAnnotationに含まれる情報を取得する方法

## はじめに
MKMapKitを使うと簡単に地図やピンが描画できてとても便利ですが、
クラスター化されたピンをタップした際のデータ取得に少し苦労したので、簡単にまとめておきます。

## 環境
Xcode 12.4
Swift 5.3.2

## 実現したかった事
- 緯度軽度とIDの情報を持ったピンを生成
- ピンのUIはシンプルに、文字の表示もしない
- 全てのピンは物理接触でクラスター化され、クラスターピンには数字を表示
- ピンをタップしたら画面遷移させ、その際ピンに含まれるIDを渡す

## 実装
まずは、カスタムアノテーションを作る。

```swift
class CustomAnnotation: NSObject, MKAnnotation {
    // クラスターID
    static let clusteringIdentifier = "locationCluster"
    // ピンの緯度軽度
    let coordinate: CLLocationCoordinate2D
    // ピンのID
    let title: String?

    init(_ coordinate: CLLocationCoordinate2D, id: Int) {
        self.coordinate = coordinate
        title = String(id)
    }
}
```

次に地図画面の実装（関係する箇所のみ）

```swift
extension MapViewController: MKMapViewDelegate {
    // ピンを地図に描画する(初期表示やデータ更新時)
    private func drawPinsOnMap() {
        let span = MKCoordinateSpan(latitudeDelta: spanLatitudeDelta, longitudeDelta: spanLongitudeDelta)
        let center = CLLocationCoordinate2DMake(centerLatitude, centerLongitude)
        let region = MKCoordinateRegion(center: center, span: span)
        mapView.region = region
        var annotations: [MKAnnotation] = []
        mapView.removeAnnotations(mapView.annotations)
        let mapData = viewModel.dataArray.value   //データ取得
        guard mapData.count > 0 else { return }
        for data in mapData {
            let pin = CustomAnnotation(CLLocationCoordinate2DMake(data.latitude, data.longitude), id: data.id)
            annotations.append(pin)
        }
        mapView.addAnnotations(annotations)
    }

    // アノテーションビューの生成
    func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
        if annotation is MKUserLocation {
            return nil
        } else if annotation is MKClusterAnnotation {
            let clusterAnnoationView = mapView.dequeueReusableAnnotationView(withIdentifier: MKMapViewDefaultClusterAnnotationViewReuseIdentifier, for: annotation)
            guard let safeCluster = clusterAnnoationView as? MKMarkerAnnotationView else { return clusterAnnoationView }
            safeCluster.titleVisibility = .hidden
            safeCluster.subtitleVisibility = .hidden
            return safeCluster
        }

        let annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: MKMapViewDefaultAnnotationViewReuseIdentifier, for: annotation)

        guard let markerAnnotationView = annotationView as? MKMarkerAnnotationView, let _ = annotation as? CustomAnnotation else {
            return annotationView
        }
        markerAnnotationView.clusteringIdentifier = CustomAnnotation.clusteringIdentifier
        markerAnnotationView.titleVisibility = .hidden
        markerAnnotationView.subtitleVisibility = .hidden
        return markerAnnotationView
    }

    // 地図上のピン押下時の処理
    func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
        var idList: [Int] = []
        // ここでクラスターピンか単体ピンか確認して処理を変える
        if let cluster = view.annotation as? MKClusterAnnotation {
            let annotations = cluster.memberAnnotations
            for annotation in annotations {
                guard let title = annotation.title as? String else { continue }
                guard let id = Int(title) else { continue }
                idList.append(id)
            }
        } else {
            guard let pin = view.annotation else { return }
            guard let title = pin.title as? String else { return }
            guard let id = Int(title) else { return }
            photoIdList.append(id)
        }

        // ★ ここでidListを持って画面遷移させる

        // 必要に応じてピンのセレクト状態を解除する
        mapView.deselectAnnotation(view.annotation, animated: true)
    }
}
```

## 結果

```swift
if let cluster = view.annotation as? MKClusterAnnotation {
    let annotations = cluster.memberAnnotations
}
```

でクラスターピンの持っている情報が取得できました。

## 感想

結果的に大したことなかった実装でしたが、クラスターピンからなかなかピンの情報が取得できず、ドキュメントを読みながら色々工夫してやっとできました。

## 参考記事

https://developer.apple.com/documentation/mapkit/mapkit_annotations

https://stackoverflow.com/questions/62606309/mkmarkerannotationview-error-terminating-app-due-to-uncaught-exception

https://teratail.com/questions/269828