【Swift】Map Kit Viewの使い方。地図上のピンをドラッグ&ドロップで移動して直線を引く。(Swift 2.1、XCode 7.2)
2020年6月16日
ピンの移動とは
前回の記事でMKMapView(以下、マップビュー)の地図に刺したピンの色や画像を変更する方法を説明した。⇒「記事」
ピンが刺さっているのを見ると、ピンを抜いて別の場所に刺し直したくなる。ピンを抜き差しできれば、ピンの位置が目的地から若干ずれているときの修正が素早く行える。
そこで、本記事ではピンをドラッグ&ドロップで別の場所に移動して表示を更新する方法について説明する。以降の手順は前回記事の続きなので、実装を試してみる人は先に読んでおくことをお勧めする。
ピンの移動を実装する
ViewController.swiftを以下のように修正する。地図に刺したピンは2つ。ドラッグ&ドロップで移動したあとに吹き出しのサブタイトルを最新の座標に更新している。
ピンをドラッグ可能にするには「アノテーションビューを返すメソッド」でアノテーションビューのdraggableプロパティをtrueに変える。
ドラッグ開始や終了のタイミングで「ドラッグ&ドロップ時のの呼び出しメソッド」がアプリから呼び出されるので、ドラッグステータスが「ドラッグ終了」のときに吹き出しのサブタイトルを更新している。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
// // ViewController.swift // import UIKit import MapKit class ViewController: UIViewController, MKMapViewDelegate { @IBOutlet weak var testMapView: MKMapView! //最初からあるメソッド override func viewDidLoad() { super.viewDidLoad() let x = 140.0 //経度 let y = 35.0 //緯度 //中心座標 let center = CLLocationCoordinate2DMake(y, x) //表示範囲 let span = MKCoordinateSpanMake(1.0, 1.0) //中心座標と表示範囲をマップに登録する。 let region = MKCoordinateRegionMake(center, span) testMapView.setRegion(region, animated:true) //左下のピン let annotation1 = TestMKPointAnnotation() annotation1.coordinate = CLLocationCoordinate2DMake(y-1.0, x-1.0) annotation1.title = "ピン1" annotation1.subtitle = "\(annotation1.coordinate.latitude), \(annotation1.coordinate.longitude)" annotation1.pinColor = UIColor.orangeColor() testMapView.addAnnotation(annotation1) //右上のピン let annotation2 = TestMKPointAnnotation() annotation2.coordinate = CLLocationCoordinate2DMake(y+1.0, x+1.0) annotation2.title = "ピン2" annotation2.subtitle = "\(annotation2.coordinate.latitude), \(annotation2.coordinate.longitude)" testMapView.addAnnotation(annotation2) annotation2.pinColor = UIColor.greenColor() //デリゲート先に自分を設定する。 testMapView.delegate = self } //アノテーションビューを返すメソッド func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? { let testView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: nil) //吹き出しを表示可能にする。 testView.canShowCallout = true //ドラッグ可能にする。 testView.draggable = true //ピンの色を設定する。 if let test = annotation as? TestMKPointAnnotation { testView.pinTintColor = test.pinColor } return testView } //ドラッグ&ドロップ時の呼び出しメソッド func mapView(mapView: MKMapView, annotationView view: MKAnnotationView, didChangeDragState newState: MKAnnotationViewDragState, fromOldState oldState: MKAnnotationViewDragState) { //ピンを離した場合 if(newState == .Ending){ if let test = view.annotation as? TestMKPointAnnotation { //ピンのサブタイトルを最新の座標にする。 test.subtitle = "\(Double(test.coordinate.latitude)), \(Double(test.coordinate.longitude))" } } } } |
以下は実際のプレイ動画
ドラッグのステータスには、以下の5種類がある。⇒「公式リファレンス」
設定名 | 説明 | |
---|---|---|
1 | None | ステート無し |
2 | Starting | ドラッグ開始 |
3 | Dragging | ドラッグ中 |
4 | Canceling | キャンセル |
5 | Ending | ドラッグ終了 |
「ドラッグ&ドロップ時の呼び出しメソッド」の中で、引数に渡ってきたMKAnnotationViewインスタンスのannotation.subtitleにそのまま値を設定しようとしてもエラーが発生する。
Cannot assign to property: 'subtitle’ is a get-only property
理由は、annotationの型がMKAnnotationプロトコルのためである。これでは値の参照しかできないので、自作クラスのTestMKPointAnnotationにキャストしてからサブタイトルを変更する。なお、色は変更しないのでMKPointAnnotationにキャストでも良い。
1 2 3 4 5 6 7 8 |
public protocol MKAnnotation : NSObjectProtocol { public var coordinate: CLLocationCoordinate2D { get } optional public var title: String? { get } optional public var subtitle: String? { get } } |
ピンとピンの間に直線を引く
サブタイトルの更新だけだと物足りないので、ピンを移動したあとにピンとピンの間に直線が引かれるようにしてみよう。
ViewController.swiftを以下のコードに変更する。「ドラッグ&ドロップ時の呼び出しメソッド」で、前回の描画の削除と、2つのピンの座標をもとに新たな描画メソッドを実行している。
「描画メソッド実行時の呼び出しメソッド」がデリゲートとして呼び出されるので、直線の色、太さを設定したMKOverlayRendererインスタンスを返している。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 |
// // ViewController.swift // import UIKit import MapKit class ViewController: UIViewController, MKMapViewDelegate { @IBOutlet weak var testMapView: MKMapView! let annotation1 = TestMKPointAnnotation() let annotation2 = TestMKPointAnnotation() var line = MKPolyline() //直線 //最初からあるメソッド override func viewDidLoad() { super.viewDidLoad() let x = 140.0 //経度 let y = 35.0 //緯度 //中心座標 let center = CLLocationCoordinate2DMake(y, x) //表示範囲 let span = MKCoordinateSpanMake(1.0, 1.0) //中心座標と表示範囲をマップに登録する。 let region = MKCoordinateRegionMake(center, span) testMapView.setRegion(region, animated:true) //左下のピン annotation1.coordinate = CLLocationCoordinate2DMake(y-1.0, x-1.0) annotation1.title = "ピン1" annotation1.subtitle = "\(annotation1.coordinate.latitude), \(annotation1.coordinate.longitude)" annotation1.pinColor = UIColor.orangeColor() testMapView.addAnnotation(annotation1) //右上のピン annotation2.coordinate = CLLocationCoordinate2DMake(y+1.0, x+1.0) annotation2.title = "ピン2" annotation2.subtitle = "\(annotation2.coordinate.latitude), \(annotation2.coordinate.longitude)" testMapView.addAnnotation(annotation2) annotation2.pinColor = UIColor.greenColor() //デリゲート先に自分を設定する。 testMapView.delegate = self } //アノテーションビューを返すメソッド func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? { let testView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: nil) //吹き出しを表示可能にする。 testView.canShowCallout = true //ドラッグ可能にする。 testView.draggable = true //ピンの色を設定する。 if let test = annotation as? TestMKPointAnnotation { testView.pinTintColor = test.pinColor } return testView } //ドラッグ&ドロップ時の呼び出しメソッド func mapView(mapView: MKMapView, annotationView view: MKAnnotationView, didChangeDragState newState: MKAnnotationViewDragState, fromOldState oldState: MKAnnotationViewDragState) { //ピンを離した場合 if(newState == .Ending){ if let test = view.annotation as? MKPointAnnotation { //ピンのサブタイトルを最新の座標にする。 test.subtitle = "\(Double(test.coordinate.latitude)), \(Double(test.coordinate.longitude))" } //前回の描画を削除する。 mapView.removeOverlay(line) //始点と終点の座標 var location:[CLLocationCoordinate2D] = [CLLocationCoordinate2D(latitude: annotation1.coordinate.latitude, longitude: annotation1.coordinate.longitude), CLLocationCoordinate2D(latitude: annotation2.coordinate.latitude, longitude: annotation2.coordinate.longitude)] //2点間に直線を描画する。 line = MKPolyline(coordinates: &location, count: 2) mapView.addOverlay(line) } } //描画メソッド実行時の呼び出しメソッド func mapView(mapView: MKMapView, rendererForOverlay overlay: MKOverlay) -> MKOverlayRenderer { let testRender = MKPolylineRenderer(overlay: overlay) //直線の幅を設定する。 testRender.lineWidth = 3 //直線の色を設定する。 testRender.strokeColor = UIColor.redColor() return testRender } } |
以下は実際のプレイ動画