MKMapSnapshotterを使ってCell内に地図を表示しピンを立てる
こんにちは。
今日はハロウィンということで、美容室に行ったらお菓子をいただきました(ヤッター)🍭🎃
最近MKMapSnapshotterについて調べることがあったので、今回はその備忘録も兼ねて記事を書いてみます。
MKMapSnapshotterはMapKit内に用意されたクラスで、地図のキャプチャ画像を生成できる仕組みです。
作りたいもの
今回は、以下のようにCollectionViewのCellの中に地図を表示し、タップしたら地図画面に遷移させる、という処理を実装してみました。 (サンプルコードにて↓の実装を公開しています。)
実は、CollectionViewやTableViewのCellに地図を表示したい際に、MKMapViewをそのまま使ってしまうと、動作がとても重くなってしまいます。ひどいと画面が固まったかのように見えます 💭💭
そこで、地図をキャプチャして画像にしてくれるMKMapSnapshotterを使うと、Viewは画像を表示するだけでよくなりスムーズに描画できるようになるのです。
MKMapSnapshotterの使い方
MKMapSnapshotterを使って画像を取得する実装はとても簡単で、座標や画像のサイズなどのオプションを指定しMKMapSnapshotterから得た画像を対象のUIImageViewにセットするだけです。
let location = CLLocationCoordinate2D(latitude: item.lat, longitude: item.lon) let span = MKCoordinateSpan(latitudeDelta: 0.005, longitudeDelta: 0.005) let options = MKMapSnapshotter.Options() options.size = CGSize(width: 400, height: 200) options.region = MKCoordinateRegion(center: location, span: span) options.scale = UIScreen.main.scale options.mapType = .standard let snapshotter = MKMapSnapshotter(options: options) snapshotter.start(completionHandler: { [weak self] (snapshot, _) in self?.mapImageView.image = snapshot?.image })
今回はその中心にピン画像も表示したかったので、MKPinAnnotationViewを使ってピンを表示する実装も追加しました。 サンプルコードはこのあたり。
snapshotter.start(completionHandler: { [weak self] (snapshot, _) in guard let snapshot = snapshot else { return } let image = UIGraphicsImageRenderer(size: options.size).image { _ in snapshot.image.draw(at: .zero) let pinView = MKPinAnnotationView(annotation: nil, reuseIdentifier: nil) let pinImage = UIImage(named: "pin")! pinView.image = pinImage var point = snapshot.point(for: location) point.x -= pinView.bounds.width / 2 point.y -= pinView.bounds.height / 2 point.x += pinView.centerOffset.x point.y += pinView.centerOffset.y pinImage.draw(at: point) } self?.mapImageView.image = image })
MKMapViewで使うMKAnnotationViewの画像を変える方法
画面全体に地図を表示する実装もとても簡単でした。サンプルコードはこちら。 ピンをオリジナルの画像にしたい場合は、MKMapViewDelegateの mapView(_:viewFor:) 内で表示したい画像に差し替える必要がありそうでした。
extension MapKitSampleMapViewController: MKMapViewDelegate { func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? { let myAnnotation = MKAnnotationView(annotation: annotation, reuseIdentifier: "pin") myAnnotation.image = UIImage(named: "pin")! myAnnotation.annotation = annotation return myAnnotation } }
MapBoxを使った似たような実装を、プライベートで開発しているYamarii(ヤマリー)の一部画面で取り入れています。 良かったら覗いてみてください〜 👀 😊