見出し画像

【徒然iOS】気ままにUIKit54〜Map Kit View 地図上のピンをドラッグ&ドロップで移動して直線を引く〜

概要

このマガジンは四十を過ぎたおっさんが、

を参考にStoryboardでiOSアプリを完全に趣味で楽しんでいるだけな記事を気ままに上げてます。

今回

をハイ、レッツゴ🕺

前準備

念の為、

  1. バックアップ

  2. 新しいクラス

  3. ビューコントローラの追加

  4. イニシャルビューの変更

をいつも通りやってから本題へ💃

てな感じかな💦

本題

⒈MapKitを配置して、アウトレット接続

こんな感じで〜〜〜

⒉コードをはめ込む

//
//  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))"
            }
        }
    }
}

を参考に〜〜〜〜
下のコードに書き換えて〜〜〜〜

今回のコード(ドラッグ)

class MapDraggableViewController: UIViewController, MKMapViewDelegate {
    @IBOutlet weak var myMapKitView: MKMapView!
    //最初からあるメソッド
    override func viewDidLoad() {
        super.viewDidLoad()
        let x = 139.692101
        let y = 35.689634
        let latitudeDelta = 10.0
        let longitudeDelta = 10.0
        //中心座標
        let center = CLLocationCoordinate2DMake(y, x)
        //表示範囲
        let span = MKCoordinateSpan(latitudeDelta: latitudeDelta, longitudeDelta: longitudeDelta)
        //中心座標と表示範囲をマップに登録する。
        let region = MKCoordinateRegion(center: center, span: span)
        myMapKitView.setRegion(region, animated:true)
        //左下のピン
        let annotation1 = MyMKPointAnnotation()
        annotation1.coordinate = CLLocationCoordinate2DMake(y-1.0, x-1.0)
        annotation1.title = "ピン1"
        annotation1.subtitle = "\(annotation1.coordinate.latitude), \(annotation1.coordinate.longitude)"
        annotation1.pinColor = UIColor.orange
        myMapKitView.addAnnotation(annotation1)
        //右上のピン
        let annotation2 = MyMKPointAnnotation()
        annotation2.coordinate = CLLocationCoordinate2DMake(y+1.0, x+1.0)
        annotation2.title = "ピン2"
        annotation2.subtitle = "\(annotation2.coordinate.latitude), \(annotation2.coordinate.longitude)"
        myMapKitView.addAnnotation(annotation2)
        annotation2.pinColor = UIColor.green
        //デリゲート先に自分を設定する。
        myMapKitView.delegate = self
    }
    //アノテーションビューを返すメソッド
    func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
        let myView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: nil)
        //吹き出しを表示可能にする。
        myView.canShowCallout = true
        //ドラッグ可能にする。
        myView.isDraggable = true
        //ピンの色を設定する。
        if let mine = annotation as? MyMKPointAnnotation {
            myView.pinTintColor = mine.pinColor
        }
        return myView
    }
    //ドラッグ&ドロップ時の呼び出しメソッド
    func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, didChange newState: MKAnnotationView.DragState, fromOldState oldState: MKAnnotationView.DragState) {
        //ピンを離した場合
        if(newState == .ending){
            if let mine = view.annotation as? MyMKPointAnnotation {
                //ピンのサブタイトルを最新の座標にする。
                mine.subtitle = "\(Double(mine.coordinate.latitude)), \(Double(mine.coordinate.longitude))"
            }
        }
    }
}

⒊シミュレータ実行

起動直後〜〜〜
移動できた〜〜〜〜

ポイント〜〜〜

こんな感じらしい

なんか
「ドラッグ&ドロップ時の呼び出しメソッド」の中で、引数に渡ってきたMKAnnotationViewインスタンスのannotation.subtitleにそのまま値を設定しようとしても
エラーが発生するらしいので、

public protocol MKAnnotation : NSObjectProtocol {
    public var coordinate: CLLocationCoordinate2D { get }
    optional public var title: String? { get }
    optional public var subtitle: String? { get }
}

を追加しとこう👀
と思って追加して、修正したら

他に影響ありまくりだねえ〜〜〜〜

一旦追加せずに、進めよう

⒋直線を引くコードに置き換え〜〜〜〜

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

正直、最初のビューはビューで残しておきたいので〜〜〜

てな感じで新しいビューを追加して
実装してく〜〜〜

今回のコード(直線まで実装)

class MapDragAndLineViewController: UIViewController, MKMapViewDelegate  {
    @IBOutlet weak var myMapKitView: MKMapView!
    let annotation1 = MyMKPointAnnotation()
    let annotation2 = MyMKPointAnnotation()
    let x = 139.692101
    let y = 35.689634
    let latitudeDelta = 10.0
    let longitudeDelta = 10.0
    var line = MKPolyline() //直線
    //最初からあるメソッド
    override func viewDidLoad() {
        super.viewDidLoad()
        //中心座標
        let center = CLLocationCoordinate2DMake(y, x)
        //表示範囲
        let span = MKCoordinateSpan(latitudeDelta: longitudeDelta, longitudeDelta: latitudeDelta)
        //中心座標と表示範囲をマップに登録する。
        let region = MKCoordinateRegion(center: center, span: span)
        myMapKitView.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.orange
        myMapKitView.addAnnotation(annotation1)
        //右上のピン
        annotation2.coordinate = CLLocationCoordinate2DMake(y+1.0, x+1.0)
        annotation2.title = "ピン2"
        annotation2.subtitle = "\(annotation2.coordinate.latitude), \(annotation2.coordinate.longitude)"
        myMapKitView.addAnnotation(annotation2)
        annotation2.pinColor = UIColor.green
        //デリゲート先に自分を設定する。
        myMapKitView.delegate = self
    }
    //アノテーションビューを返すメソッド
    func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
        let myView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: nil)
        //吹き出しを表示可能にする。
        myView.canShowCallout = true
        //ドラッグ可能にする。
        myView.isDraggable = true
        //ピンの色を設定する。
        if let mine = annotation as? MyMKPointAnnotation {
            myView.pinTintColor = mine.pinColor
        }
        return myView
    }
    //ドラッグ&ドロップ時の呼び出しメソッド
    func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, didChange newState: MKAnnotationView.DragState, fromOldState oldState: MKAnnotationView.DragState) {
        //ピンを離した場合
        if(newState == .ending){
            if let mine = view.annotation as? MKPointAnnotation {
                //ピンのサブタイトルを最新の座標にする。
                mine.subtitle = "\(Double(mine.coordinate.latitude)), \(Double(mine.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, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
        let myRender = MKPolylineRenderer(overlay: overlay)
        //直線の幅を設定する。
        myRender.lineWidth = 3
        //直線の色を設定する。
        myRender.strokeColor = UIColor.red
        return myRender
    }
}

⒌シミュレータで実行

問題なし。エラーなし👀
実機も問題なし🕺

以上。

ブラッシュアップ

今回もここまででやっているので特になしのため割愛💦

今回のポイント

サイト記事と見比べてもわかるとおり、
当時の記事はそうでも、すでにバージョンで改善してる可能性もあるし、
不要なプロトコルをpublicなんかで組んでしまうと、他に影響が出てくる可能性も高い。
👉単体で使うならば問題ないかもしれないけど、
ビューが一つしかないとか機能が一つしかないアプリなんてないので、

結合まで視野に入れながら、必要なプロトコルを組もう

Apple公式

さて、次回は

をレッツゴする🕺

最近の記事は、

ドラッグだったり、タップだったり、これまでの記事では出てきてない、後の記事で説明されてる機能を先取って組み合わせてるものが多いね👀
まあ、これを機会に触れちゃおう🕺

逆に、MapならMapだけの機能で全てが成立してるわけじゃない
👉システムもアプリも

色即是空

てことがわかって良きや良き🕺

この記事が気に入ったらサポートをしてみませんか?