【徒然iOS】気ままにUIKit63〜Map Kit View ピンの移動をアニメーションさせてリアル感を出す〜
概要
このマガジンは四十を過ぎたおっさんが、
を参考にStoryboardでiOSアプリを完全に趣味で楽しんでいるだけな記事を気ままに上げてます。
今回
をハイ、レッツゴ🕺
MapKitも今回で最後だね〜〜〜〜(しんみり🤤)
前準備
念の為、
バックアップ
新しいクラス
ビューコントローラの追加
イニシャルビューの変更
をいつも通りやってから本題へ💃
本題
ピンの移動にアニメーションを使う方法
を今回はやるみたいだね👀
⒈いつもどおりMapKitとLongPressGestureを配置して、接続〜〜〜
流石にここはこれまでの記事で何回も出てきてるので、詳細は貼らない💦
ここから先は、
今回は、大きく3つに分かれるみたいだからそれぞれ大きな見出しに分けてやってく〜〜〜〜🕺
⒉ピンを刺すときのアニメーション
①コードを組み込む
//
// ViewController.swift
//
import UIKit
import MapKit
class ViewController: UIViewController,MKMapViewDelegate {
@IBOutlet weak var testMapView: MKMapView!
var pinView:MKPinAnnotationView!
//最初からあるメソッド
override func viewDidLoad() {
super.viewDidLoad()
//中心座標
let center = CLLocationCoordinate2DMake(35.0, 140.0)
//表示範囲
let span = MKCoordinateSpanMake(1.0, 1.0)
//中心座標と表示範囲をマップに登録する。
let region = MKCoordinateRegionMake(center, span)
testMapView.setRegion(region, animated:true)
//デリゲート先に自分を設定する。
testMapView.delegate = self
}
//マップビュー長押し時の呼び出しメソッド
@IBAction func pressMap(sender: UILongPressGestureRecognizer) {
if (sender.state == UIGestureRecognizerState.Ended){
//マップビュー内のタップした位置を取得する。
let location:CGPoint = sender.locationInView(testMapView)
//タップした位置を緯度、経度の座標に変換する。
let mapPoint:CLLocationCoordinate2D = testMapView.convertPoint(location, toCoordinateFromView: testMapView)
let annotation = MKPointAnnotation()
//ピンを作成してマップビューに登録する。
annotation.coordinate = CLLocationCoordinate2DMake(mapPoint.latitude, mapPoint.longitude)
annotation.title = "ピン"
testMapView.addAnnotation(annotation)
}
}
//アノテーションビューを返すメソッド
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
//アノテーションビューを作成する。
pinView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: "testPin")
//吹き出しを表示可能にする。
pinView.canShowCallout = true
//ピンのアニメーションをONにする。
pinView.animatesDrop = true
return pinView
}
}
を参考に💃
②シミュレータを実行
今回のコード(最初のアニメーション)
class MapAnimationViewController: UIViewController,MKMapViewDelegate {
@IBOutlet weak var myMapKitView: MKMapView!
let x = 139.744858
let y = 35.675888
let latitudeDelta = 0.01
let longitudeDelta = 0.01
var pinView:MKPinAnnotationView!
//最初からあるメソッド
override func viewDidLoad() {
super.viewDidLoad()
//中心座標
let center = CLLocationCoordinate2DMake(y, x)
//表示範囲
let span = MKCoordinateSpan(latitudeDelta: latitudeDelta, longitudeDelta: longitudeDelta)
//中心座標と表示範囲をマップに登録する。
let region = MKCoordinateRegion(center: center, span: span)
myMapKitView.setRegion(region, animated:true)
//デリゲート先に自分を設定する。
myMapKitView.delegate = self
}
@IBAction func myLongTap(_ sender: UILongPressGestureRecognizer) {
if (sender.state == UIGestureRecognizer.State.ended){
//マップビュー内のタップした位置を取得する。
let location:CGPoint = sender.location(in: myMapKitView)
//タップした位置を緯度、経度の座標に変換する。
let mapPoint:CLLocationCoordinate2D = myMapKitView.convert(location, toCoordinateFrom: myMapKitView)
let annotation = MKPointAnnotation()
//ピンを作成してマップビューに登録する。
annotation.coordinate = CLLocationCoordinate2DMake(mapPoint.latitude, mapPoint.longitude)
annotation.title = "ピン"
myMapKitView.addAnnotation(annotation)
}
}
//アノテーションビューを返すメソッド
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
//アノテーションビューを作成する。
pinView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: "testPin")
//吹き出しを表示可能にする。
pinView.canShowCallout = true
//ピンのアニメーションをONにする。
pinView.animatesDrop = true
return pinView
}
}
⒊ピン失敗アニメーション
失敗アニメーションのコードでやってみよう
①コードを追加
//アノテーションビューの追加終了時の呼び出しメソッド
func mapView(mapView: MKMapView, didAddAnnotationViews views: [MKAnnotationView]) {
//アニメーションで90度回転させる
UIView.animateWithDuration(1.0, animations: { () -> Void in
self.pinView.transform = CGAffineTransformMakeRotation(CGFloat(M_PI/2.0))
}, completion: {
(finished: Bool) in
//回転が終わったらピンの画像を変える。
self.pinView.image = UIImage(named: "pin_img.png")
}
)
}
を追加して〜〜〜書き換え〜〜〜
②シミュレータを実行
今回のコード(失敗アニメーション)
class MapAnimationViewController: UIViewController,MKMapViewDelegate {
@IBOutlet weak var myMapKitView: MKMapView!
let x = 139.744858
let y = 35.675888
let latitudeDelta = 0.01
let longitudeDelta = 0.01
var pinView:MKPinAnnotationView!
//最初からあるメソッド
override func viewDidLoad() {
super.viewDidLoad()
//中心座標
let center = CLLocationCoordinate2DMake(y, x)
//表示範囲
let span = MKCoordinateSpan(latitudeDelta: latitudeDelta, longitudeDelta: longitudeDelta)
//中心座標と表示範囲をマップに登録する。
let region = MKCoordinateRegion(center: center, span: span)
myMapKitView.setRegion(region, animated:true)
//デリゲート先に自分を設定する。
myMapKitView.delegate = self
}
@IBAction func myLongTap(_ sender: UILongPressGestureRecognizer) {
if (sender.state == UIGestureRecognizer.State.ended){
//マップビュー内のタップした位置を取得する。
let location:CGPoint = sender.location(in: myMapKitView)
//タップした位置を緯度、経度の座標に変換する。
let mapPoint:CLLocationCoordinate2D = myMapKitView.convert(location, toCoordinateFrom: myMapKitView)
let annotation = MKPointAnnotation()
//ピンを作成してマップビューに登録する。
annotation.coordinate = CLLocationCoordinate2DMake(mapPoint.latitude, mapPoint.longitude)
annotation.title = "ピン"
myMapKitView.addAnnotation(annotation)
}
}
//アノテーションビューを返すメソッド
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
//アノテーションビューを作成する。
pinView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: "testPin")
//吹き出しを表示可能にする。
pinView.canShowCallout = true
//ピンのアニメーションをONにする。
pinView.animatesDrop = true
return pinView
}
//アノテーションビューの追加終了時の呼び出しメソッド
func mapView(_ mapView: MKMapView, didAdd views: [MKAnnotationView]) {
//アニメーションで90度回転させる
UIView.animate(withDuration: 1.0, animations: { () -> Void in
self.pinView.transform = CGAffineTransformMakeRotation(CGFloat(Double.pi/2.0))
}, completion: {
(finished: Bool) in
//回転が終わったらピンの画像を変える。
self.pinView.image = UIImage(named: "pin_img.png")
}
)
}
}
⒋ピンを移動するときのアニメーション
①コードを組み込む
//
// ViewController.swift
//
import UIKit
import MapKit
class ViewController: UIViewController,MKMapViewDelegate {
@IBOutlet weak var testMapView: MKMapView!
var pinView:MKPinAnnotationView!
var annotation:MKPointAnnotation!
//最初からあるメソッド
override func viewDidLoad() {
super.viewDidLoad()
//中心座標
let center = CLLocationCoordinate2DMake(35.0, 140.0)
//表示範囲
let span = MKCoordinateSpanMake(1.0, 1.0)
//中心座標と表示範囲をマップに登録する。
let region = MKCoordinateRegionMake(center, span)
testMapView.setRegion(region, animated:true)
//デリゲート先に自分を設定する。
testMapView.delegate = self
}
//マップビュー長押し時の呼び出しメソッド
@IBAction func pressMap(sender: UILongPressGestureRecognizer) {
if (sender.state == UIGestureRecognizerState.Ended){
//マップビュー内のタップした位置を取得する。
let location:CGPoint = sender.locationInView(testMapView)
//タップした位置を緯度、経度の座標に変換する。
let mapPoint:CLLocationCoordinate2D = testMapView.convertPoint(location, toCoordinateFromView: testMapView)
if (annotation == nil) {
//ピンが無いときは作成してマップビューに登録する。
annotation = MKPointAnnotation()
annotation.coordinate = CLLocationCoordinate2DMake(mapPoint.latitude, mapPoint.longitude)
annotation.title = "ピン"
testMapView.addAnnotation(annotation)
} else {
//長押しした座標までアニメーションを使って移動する。
UIView.animateWithDuration(1.0, animations: { () -> Void in
self.annotation.coordinate = CLLocationCoordinate2DMake(mapPoint.latitude, mapPoint.longitude)
}, completion:nil)
}
}
}
//アノテーションビューを返すメソッド
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
//アノテーションビューを作成する。
pinView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: "testPin")
//吹き出しを表示可能にする。
pinView.canShowCallout = true
//ピンのアニメーションをONにする。
pinView.animatesDrop = true
return pinView
}
}
②シミュレータを実行
今回のコード(移動アニメーション)
class MapAnimationViewController: UIViewController,MKMapViewDelegate {
@IBOutlet weak var myMapKitView: MKMapView!
let x = 139.744858
let y = 35.675888
let latitudeDelta = 0.01
let longitudeDelta = 0.01
var pinView:MKPinAnnotationView!
var annotation:MKPointAnnotation!
//最初からあるメソッド
override func viewDidLoad() {
super.viewDidLoad()
//中心座標
let center = CLLocationCoordinate2DMake(y, x)
//表示範囲
let span = MKCoordinateSpan(latitudeDelta: latitudeDelta, longitudeDelta: longitudeDelta)
//中心座標と表示範囲をマップに登録する。
let region = MKCoordinateRegion(center: center, span: span)
myMapKitView.setRegion(region, animated:true)
//デリゲート先に自分を設定する。
myMapKitView.delegate = self
}
//マップビュー長押し時の呼び出しメソッド
@IBAction func myLongTap(_ sender: UILongPressGestureRecognizer) {
if (sender.state == UIGestureRecognizer.State.ended){
//マップビュー内のタップした位置を取得する。
let location:CGPoint = sender.location(in: myMapKitView)
//タップした位置を緯度、経度の座標に変換する。
let mapPoint:CLLocationCoordinate2D = myMapKitView.convert(location, toCoordinateFrom: myMapKitView)
if (annotation == nil) {
//ピンが無いときは作成してマップビューに登録する。
annotation = MKPointAnnotation()
annotation.coordinate = CLLocationCoordinate2DMake(mapPoint.latitude, mapPoint.longitude)
annotation.title = "ピン"
myMapKitView.addAnnotation(annotation)
} else {
//長押しした座標までアニメーションを使って移動する。
UIView.animate(withDuration: 1.0, animations: { () -> Void in
self.annotation.coordinate = CLLocationCoordinate2DMake(mapPoint.latitude, mapPoint.longitude)
}, completion:nil)
}
}
}
//アノテーションビューを返すメソッド
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
//アノテーションビューを作成する。
pinView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: "testPin")
//吹き出しを表示可能にする。
pinView.canShowCallout = true
//ピンのアニメーションをONにする。
pinView.animatesDrop = true
return pinView
}
}
測地線に沿ってピンを動かす
測地線とは、
2地点の最短距離を表す線のこと
らしい👀
MKGeodesicPolyline(以下、GEOライン)を使うと、2地点の座標だけで測地線を地図上に描くことができる
んだって🕺
っと、サイト記事で確認したけど、ここは、長押しタップを使わないみたいなんで、もうひとつビューを追加すっかな💦
①コードを組み込む
//
// ViewController.swift
//
import UIKit
import MapKit
class ViewController: UIViewController,MKMapViewDelegate {
@IBOutlet weak var testMapView: MKMapView!
var pinView:MKPinAnnotationView!
var annotation:MKPointAnnotation!
var geoLine: MKGeodesicPolyline!
var pinPosition:Int = 0
//最初からあるメソッド
override func viewDidLoad() {
super.viewDidLoad()
//日本とロンドンの空港の座標
var coordinate = [CLLocationCoordinate2D(latitude: 35.549393, longitude: 139.779839),
CLLocationCoordinate2D(latitude: 51.470022, longitude: 0.454296)]
//測地線をマップに追加する。
geoLine = MKGeodesicPolyline(coordinates: &coordinate, count: 2)
testMapView.addOverlay(geoLine)
print(geoLine.pointCount)
//中心座標
let center = CLLocationCoordinate2DMake(35.0, 139.0)
//表示範囲
let span = MKCoordinateSpanMake(5.0, 5.0)
//中心座標と表示範囲をマップに登録する。
let region = MKCoordinateRegionMake(center, span)
testMapView.setRegion(region, animated:true)
//ピンをマップビューに追加する。
annotation = MKPointAnnotation()
testMapView.addAnnotation(annotation)
//デリゲート先に自分を設定する。
testMapView.delegate = self
//ピンの移動を開始する。
movePin()
}
//描画メソッド実行時の呼び出しメソッド
func mapView(mapView: MKMapView, rendererForOverlay overlay: MKOverlay) -> MKOverlayRenderer {
let renderer = MKPolylineRenderer(overlay: overlay)
renderer.lineWidth = 3.0
renderer.strokeColor = UIColor.blueColor()
return renderer
}
//ピンを移動するメソッド
func movePin() {
//終点に到着したら移動を終了する。
if pinPosition > geoLine.pointCount {
return
}
//測地線上の座標の配列を取得する。
let geoPoints = geoLine.points()
//移動先の座標をピンに設定する。
annotation.coordinate = MKCoordinateForMapPoint(geoPoints[pinPosition])
pinPosition += 20
//メソッドを再度呼び出す。
performSelector("movePin", withObject:nil, afterDelay:0.05)
}
}
を参考に🕺書き換え〜〜〜
②シミュレータを実行
今回のコード(測地線)
class MapGeodesicPolylineViewController: UIViewController,MKMapViewDelegate {
@IBOutlet weak var myMapKitView: MKMapView!
var pinView:MKPinAnnotationView!
let latitudeDelta = 5.0
let longitudeDelta = 5.0
var annotation:MKPointAnnotation!
var geoLine: MKGeodesicPolyline!
var pinPosition:Int = 0
//最初からあるメソッド
override func viewDidLoad() {
super.viewDidLoad()
//日本とロンドンの空港の座標
var coordinate = [CLLocationCoordinate2D(latitude: 35.549393, longitude: 139.779839),
CLLocationCoordinate2D(latitude: 51.470022, longitude: 0.454296)]
//測地線をマップに追加する。
geoLine = MKGeodesicPolyline(coordinates: &coordinate, count: 2)
myMapKitView.addOverlay(geoLine)
print(geoLine.pointCount)
//中心座標
let center = CLLocationCoordinate2DMake(35.0, 139.0)
//表示範囲
let span = MKCoordinateSpan(latitudeDelta: latitudeDelta, longitudeDelta: longitudeDelta)
//中心座標と表示範囲をマップに登録する。
let region = MKCoordinateRegion(center: center, span: span)
myMapKitView.setRegion(region, animated:true)
//ピンをマップビューに追加する。
annotation = MKPointAnnotation()
myMapKitView.addAnnotation(annotation)
//デリゲート先に自分を設定する。
myMapKitView.delegate = self
//ピンの移動を開始する。
movePin()
}
//描画メソッド実行時の呼び出しメソッド
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
let renderer = MKPolylineRenderer(overlay: overlay)
renderer.lineWidth = 3.0
renderer.strokeColor = UIColor.blue
return renderer
}
//ピンを移動するメソッド
@objc func movePin() {
//終点に到着したら移動を終了する。
if pinPosition > geoLine.pointCount {
return
}
//測地線上の座標の配列を取得する。
let geoPoints = geoLine.points()
//移動先の座標をピンに設定する。
annotation.coordinate = geoPoints[pinPosition].coordinate
pinPosition += 20
//メソッドを再度呼び出す。
perform(#selector(self.movePin), with:nil, afterDelay:0.05)
}
//アノテーションビューを返すメソッド
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
//アノテーションビューを作成する。
pinView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: "testPin")
//吹き出しを表示可能にする。
pinView.canShowCallout = true
//ピンのアニメーションをONにする。
pinView.animatesDrop = true
return pinView
}
}
サイト記事の内容としては以上かな💦
ブラッシュアップ
今回も特にここまででやっているので割愛。むしろ、コード改修部分を大事にして欲しいかな💦
しれっと
//ピンを移動するメソッド
@objc func movePin() {
//終点に到着したら移動を終了する。
if pinPosition > geoLine.pointCount {
return
}
//測地線上の座標の配列を取得する。
let geoPoints = geoLine.points()
//移動先の座標をピンに設定する。
annotation.coordinate = geoPoints[pinPosition].coordinate
pinPosition += 20
//メソッドを再度呼び出す。
perform(#selector(self.movePin), with:nil, afterDelay:0.05)
}
に書き換えないといけなかったので💦
まあ、理解しておけば大して難しくないんだけど。
Apple公式
さて、次回は
をレッツゴする🕺
駆け足だったけど、MapKitも終わったね〜〜〜
Gestureに関しては、全8回かあ🤔
Tap GestureとかLongPressGestureとか実は今までも結構出てきてるんだよね〜〜〜、、、💦ま、
💃楽しく作ってこ🕺
犬の散歩後、、、
ちょっと、うちの犬をロンドンに旅立たせてみたくなったので〜〜〜
を参考に、、、
コードを書き換えてシミュレータ実行
とまあ、こういう遊びもできたりするね✨
お遊びコード(ちゃん丸巣立ちの日)
class MapChanmaruViewController: UIViewController,MKMapViewDelegate {
@IBOutlet weak var myMapKitView: MKMapView!
var pinView:MKPinAnnotationView!
let annotation = MyMKPointAnnotation()
let latitudeDelta = 5.0
let longitudeDelta = 5.0
var geoLine: MKGeodesicPolyline!
var pinPosition:Int = 0
//最初からあるメソッド
override func viewDidLoad() {
super.viewDidLoad()
//日本とロンドンの空港の座標
var coordinate = [
CLLocationCoordinate2D(latitude: 35.549393, longitude: 139.779839),
CLLocationCoordinate2D(latitude: 51.470022, longitude: 0.454296)
]
//測地線をマップに追加する。
geoLine = MKGeodesicPolyline(coordinates: &coordinate, count: 2)
myMapKitView.addOverlay(geoLine)
print(geoLine.pointCount)
//中心座標
let center = CLLocationCoordinate2DMake(35.0, 139.0)
//表示範囲
let span = MKCoordinateSpan(latitudeDelta: latitudeDelta, longitudeDelta: longitudeDelta)
//中心座標と表示範囲をマップに登録する。
let region = MKCoordinateRegion(center: center, span: span)
myMapKitView.setRegion(region, animated:true)
//中心のピン(画像を設定)
annotation.coordinate = CLLocationCoordinate2DMake(35.0, 139.0)
annotation.title = "中心"
annotation.subtitle = "\(annotation.coordinate.latitude), \(annotation.coordinate.longitude)"
annotation.pinImage = "ちゃん丸"
myMapKitView.addAnnotation(annotation)
//デリゲート先に自分を設定する。
myMapKitView.delegate = self
//ピンの移動を開始する。
movePin()
}
//描画メソッド実行時の呼び出しメソッド
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
let renderer = MKPolylineRenderer(overlay: overlay)
renderer.lineWidth = 3.0
renderer.strokeColor = UIColor.blue
return renderer
}
//ピンを移動するメソッド
@objc func movePin() {
//終点に到着したら移動を終了する。
if pinPosition > geoLine.pointCount {
return
}
//測地線上の座標の配列を取得する。
let geoPoints = geoLine.points()
//移動先の座標をピンに設定する。
annotation.coordinate = geoPoints[pinPosition].coordinate
pinPosition += 20
//メソッドを再度呼び出す。
perform(#selector(self.movePin), with:nil, afterDelay:0.05)
}
//アノテーションビューを返すメソッド
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
//アノテーションビューを作成する。
if let mine = annotation as? MyMKPointAnnotation {
if(mine.pinColor != nil) {
//色が設定されている場合
let myPinView = MKPinAnnotationView()
myPinView.annotation = annotation
myPinView.pinTintColor = mine.pinColor
myPinView.canShowCallout = true
return myPinView
} else if(mine.pinImage != nil) {
//画像が設定されている場合
let myImageView = MKAnnotationView()
myImageView.annotation = annotation
myImageView.image = UIImage(named:mine.pinImage)
myImageView.canShowCallout = true
return myImageView
}
}
//色も画像も設定されていない場合
let myPinView = MKPinAnnotationView()
//吹き出しを表示可能にする。
pinView.canShowCallout = true
myPinView.annotation = annotation
//ピンのアニメーションをONにする。
pinView.animatesDrop = true
return pinView
}
}
class MyMKPointAnnotation: MKPointAnnotation {
//ピンの色
var pinColor:UIColor!
//ピンの画像
var pinImage:String!
}
色々遊べるから試してみてね〜〜〜
この記事が気に入ったらサポートをしてみませんか?