検証環境:
Xcode 12.4
Swift 5.3.2
iOS Deployment Target 14.4
マップ(MKMapView)上のピンの画像をデフォルトのものではなく別の画像にカスタマイズする方法について。
iOS11からマップビューにデフォルトでMKMarkerAnnotationView
が登録されているので、mapView.dequeueReusableAnnotationView
で取得し、glyphImage
プロパティに画像を設定すればよい。
画像は iOS13 から追加された SF Symbols の画像を使って置き換えた。
またピンの色はmarkerTintColor
を設定することで変更できる。
コードは前回のサンプルコードに追記していく形で。
xyk.hatenablog.com
サンプルコード
import UIKit import MapKit class ViewController: UIViewController { let mapView = MKMapView() override func viewDidLoad() { super.viewDidLoad() view.addSubview(mapView) mapView.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ mapView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor), mapView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor), mapView.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor), mapView.rightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.rightAnchor), ]) mapView.delegate = self let center = CLLocationCoordinate2D(latitude: 35.68358493824179, longitude: 139.750327090532) mapView.region = MKCoordinateRegion(center: center, latitudinalMeters: 500, longitudinalMeters: 500) let longPress = UILongPressGestureRecognizer(target: self, action: #selector(didLongPress)) longPress.minimumPressDuration = 0.5 mapView.addGestureRecognizer(longPress) } @objc private func didLongPress(_ gesture: UILongPressGestureRecognizer) { guard gesture.state == .began else { return } let annotation = MKPointAnnotation() let location = gesture.location(in: mapView) let coordinate = mapView.convert(location, toCoordinateFrom: mapView) annotation.coordinate = coordinate annotation.title = "\(mapView.annotations.count)" mapView.addAnnotation(annotation) } } extension ViewController: MKMapViewDelegate { func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? { let annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: MKMapViewDefaultAnnotationViewReuseIdentifier, for: annotation) as! MKMarkerAnnotationView annotationView.displayPriority = .required annotationView.glyphImage = UIImage(systemName: "flame")! // SF Symbols の画像を使用 annotationView.markerTintColor = selectTintColor(annotation) // 色の変更 return annotationView } private func selectTintColor(_ annotation: MKAnnotation?) -> UIColor? { guard let annotation = annotation as? MKPointAnnotation else { return nil } let colors: [UIColor] = [.systemRed, .systemBlue, .systemYellow, .systemGreen] let index = Int(annotation.title ?? "") ?? 0 let remainder = index % colors.count return colors[remainder] } }
MKMarkerAnnotationView を継承したカスタムクラスを用意した場合のサンプルコード
カスタムクラス内で画像や色を設定すれば MKMapViewDelegate の実装は不要。
import UIKit import MapKit class ViewController: UIViewController { let mapView = MKMapView() override func viewDidLoad() { super.viewDidLoad() view.addSubview(mapView) mapView.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ mapView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor), mapView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor), mapView.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor), mapView.rightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.rightAnchor), ]) let center = CLLocationCoordinate2D(latitude: 35.68358493824179, longitude: 139.750327090532) mapView.region = MKCoordinateRegion(center: center, latitudinalMeters: 500, longitudinalMeters: 500) let longPress = UILongPressGestureRecognizer(target: self, action: #selector(didLongPress)) longPress.minimumPressDuration = 0.5 mapView.addGestureRecognizer(longPress) // カスタムアノテーションビューを登録 mapView.register(CustomAnnotationView.self, forAnnotationViewWithReuseIdentifier: MKMapViewDefaultAnnotationViewReuseIdentifier) } @objc private func didLongPress(_ gesture: UILongPressGestureRecognizer) { guard gesture.state == .began else { return } let annotation = MKPointAnnotation() let location = gesture.location(in: mapView) let coordinate = mapView.convert(location, toCoordinateFrom: mapView) annotation.coordinate = coordinate annotation.title = "\(mapView.annotations.count)" mapView.addAnnotation(annotation) } } // カスタムアノテーションビューの定義 class CustomAnnotationView: MKMarkerAnnotationView { override var annotation: MKAnnotation? { didSet { configure(for: annotation) } } override init(annotation: MKAnnotation?, reuseIdentifier: String?) { super.init(annotation: annotation, reuseIdentifier: reuseIdentifier) glyphImage = UIImage(systemName: "flame")! configure(for: annotation) } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } func configure(for annotation: MKAnnotation?) { displayPriority = .required markerTintColor = selectTintColor(annotation) } private func selectTintColor(_ annotation: MKAnnotation?) -> UIColor? { guard let annotation = annotation as? MKPointAnnotation else { return nil } let colors: [UIColor] = [.systemRed, .systemBlue, .systemYellow, .systemGreen] let index = Int(annotation.title ?? "") ?? 0 let remainder = index % colors.count return colors[remainder] } }