投稿もプログラミングも初心者です…もっとこうした方がいい、こういう書き方のほうがいい、ここは違う!等ありましたらご指導よろしくお願いします。
XcodeはVersion 8.2.1
SwiftはVersion 3.0.2 です。
前回の記事の続きとして書いていきたいと思います。
前回はユーザーの位置情報を取得して現在地を地図上に表示するところまで実装しました。
今回はこの地図の任意の場所にロングタップ(長押し)でピンを立てられるようにしたいと思います。
#LongPressGestureRecognizer(長押し認識)の導入
- ストーリーボード上のViewControllerにLongPressGestureRecognizer導入
- ストーリーボードとコードを紐付け
- 実際に起動してロングタップを感知しているか確認
の順にやっていきます
##1.ストーリーボード上のViewControllerにLongPressGestureRecognizerを導入
まずはXcodeの左側の Main.storyboard を選択してください。
次に画面右下のObject libraryの検索ボックスに「long」まで入力すれば、LongPressGestureRecognizerが表示されると思います。
右下に表示されたLongPressGestureRecognizerを ストーリーボード上のMapViewの上にドラッグアンドドロップ してください。
##2.ストーリーボード上のLongPressGestureRecognizerとコードを紐付け
ロングタップされたときにコード上で色々な処理ができるようにストーリーボード上LongPressGestureRecognizerとコードを紐付けます。
Xcode画面左側(Project Navigatorと言うらしい)のMain.storyboardが選択された状態で、右上ののアイコンをクリックしてストーリーボードの右側にコード画面を出してください。
先程、3つのアイコンのところにドラッグアンドドロップして増えたアイコン(右から2番目の灰色の中に青い丸のようなアイコン)をcontrolキーを押しながらViewControllerクラスの中にドラッグアンドドロップしましょう。
上の画面になったらConnectionをActionを選択して名前をlongPressMapとします。
そしてViewControllerに次のように記述します。
class ViewController: UIViewController, CLLocationManagerDelegate, MKMapViewDelegate{
@IBOutlet weak var testMapView: MKMapView!
//CLLocationManagerを定義
var myLocationManager:CLLocationManager!
//タップされた回数
var tapped = 1
@IBAction func longPressMap(_ sender: UILongPressGestureRecognizer) {
//ロングタップの最初の感知のみ受け取る
if(sender.state != UIGestureRecognizerState.began){
return
}
//ロングタップを検出したことと回数をログに表示
print("long tapped \(tapped)")
tapped += 1
}
}
if文のところは、senderの状態がロングタップを最初に感知したときのみに次の処理(ログにロングタップを感知したことと何回目かを表示)がされるようにしています。なぜbeganなのかというと、ロングタップは長押しをしたとき、長押しをしてから指を離さずに移動したとき、長押しを感知してから指を離したときの3パターンで感知されるので、必要以上にピンを立ててしまわないように1回のロングタップで1回の感知のみにしています。if文をコメントアウトしてRunしてみれば3パターン全てのときで感知するのが確かめられると思います。
##3.地図の長押しした場所にピンを立てる!
では実際にピンを立てるところまでやってみましょう。
次のコードの/* ここから追加 */
や/* 追加 */
のところを追記しました。
class ViewController: UIViewController, CLLocationManagerDelegate, MKMapViewDelegate{
@IBOutlet weak var testMapView: MKMapView!
//CLLocationManagerを定義
var myLocationManager:CLLocationManager!
/* 追加 */
//ロングタップしたときに立てるピンを定義
var pinByLongPress:MKPointAnnotation!
override func viewDidLoad() {
super.viewDidLoad()
//CLLocationManagerをインスタンス化
myLocationManager = CLLocationManager()
//位置情報使用許可のリクエストを表示
myLocationManager.requestWhenInUseAuthorization()
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("error")
}
//ロングタップを感知したときに呼び出されるメソッド
@IBAction func longPressMap(_ sender: UILongPressGestureRecognizer) {
//ロングタップの最初の感知のみ受け取る
if(sender.state != UIGestureRecognizerState.began){
return
}
/* ここから追加 */
//インスタンス化
pinByLongPress = MKPointAnnotation()
//ロングタップから位置情報を取得
let location:CGPoint = sender.location(in: testMapView)
//取得した位置情報をCLLocationCoordinate2D(座標)に変換
let longPressedCoordinate:CLLocationCoordinate2D = testMapView.convert(location, toCoordinateFrom: testMapView)
//ロングタップした位置の座標をピンに入力
pinByLongPress.coordinate = longPressedCoordinate
//ピンを追加する(立てる)
testMapView.addAnnotation(pinByLongPress)
/* ここまで追加 */
}
}
さらに以下のようにするとピンを立てる際にアニメーションをしたりします(雑でスミマセン)
override func viewDidLoad() {
super.viewDidLoad()
//CLLocationManagerをインスタンス化
myLocationManager = CLLocationManager()
//位置情報使用許可のリクエストを表示
myLocationManager.requestWhenInUseAuthorization()
/* ここから追加 */
testMapView.delegate = self
//
myLocationManager.delegate = self
/* ここまで追加 */
}
//addAnnotationした際に呼ばれるデリゲートメソッド
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if annotation === mapView.userLocation {
// 現在地を示すアノテーションの場合はデフォルトのまま
//現在地のタイトルをnilにすることでコールアウトを非表示にする
(annotation as? MKUserLocation)?.title = nil
return nil //nilを返すことで現在地がピンにならない
} else {
let identifier = "annotation"
if let annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: identifier){
// 再利用できる場合はそのまま返す
return annotationView
} else { // 再利用できるアノテーションが無い場合(初回など)は生成する
let myPinIdentifier = "PinAnnotationIdentifier"
//ピンをインスタンス化
let pinByLongPress = MKPointAnnotation()
//アノテーションビュー生成
let annotationView = MKPinAnnotationView(annotation: pinByLongPress, reuseIdentifier: myPinIdentifier)
//ピンが降ってくるアニメーションをつける
annotationView.animatesDrop = true
return annotationView
}
}
}