koogawa blog

iOS、Android、foursquareに関する話題

Swift と MapKit で地図アプリを作成する(前編)

今回はMapKitを使って、簡単な地図アプリを作ってみます。

アプリの仕様

  • アプリ全面に地図を表示する
  • 地図にピンを立てる
  • ピンをタップするとバルーンが表示される

Simulator Screen Shot 2015.10.11 10.02.59.png

開発環境

  • Xcode 7.1 (7B91b)
  • Swift 2.0

ソースコード

今回作成するアプリのソースコードGitHubにアップしております。

https://github.com/koogawa/MKMapViewSample

準備

1. プロジェクト作成

Xcode のメニューから File > New > Project を選択します。今回は「Single View Application」を選択します。

kobito.1446271863.945207.png

プロジェクトの情報を入力します。

kobito.1446272023.785637.png

  • Product Name:今回は「MKMapViewSample」としました
  • Organization Name:あなたの組織名
  • Organization Identifier:サンプルアプリなので適当で良いです
  • Language:Swift
  • Devices:iPhone

保存場所を聞かれるので、適当な場所に保存します。Gitのチェックボックスはとりあえず外しておいて大丈夫です。

2. MapKit.framework追加

地図を扱うために、MapKit.framework を Linked Frameworks and Libraries に追加します。

スクリーンショット 2015-10-31 20.10.34.png

3. MapView貼り付け

画面左のソースツリーから Main.storyboard を選択します。

kobito.1446272617.494998.png

View の上に Map Kit View をドラッグします。

EAAD7425-39BC-401D-8D12-8BC33C9D7A60.png

これで画面に地図を表示するアプリができました。いったんビルドしてアプリを動かしてみましょう。(Command+R)

Simulator Screen Shot 2015.11.01 0.17.45.png

おっと、地図のサイズがおかしいですね。 Auto Layout を使ってサイズを調整していきましょう。

Story Board の MapViewを選択した状態で、画面右下のPinボタンをクリックします。制約を設定するポップアップが出てくるので、上下左右の余白をすべて 0 にしましょう。マージンは必要ないので「Constrain to margins」にはチェックを入れないようにしてください。ボタンが「Add 4 Constrains」になっていることを確認してクリックします。

スクリーンショット 2015-10-31 20.15.58.png

Story Board 上の MapView サイズも制約に合わせて調整します。 MapViewを選択した状態で、画面右下の「Resolve Auto Layout Issues」ボタンをクリックします。

Selected Views の「Update Frames」をクリックします。

スクリーンショット 2015-10-31 20.16.35.png

これで地図が画面いっぱいに表示されるようになります。

Simulator Screen Shot 2015.11.01 0.47.11.png

実装

ここからはコードを少し書いていきます。エディタで ViewController.swift を開いておいてください。

インポート

まずは、地図を扱えるように MapKit をインポートします。

import MapKit

Story Boardとソースコード結びつけ

次に、画面右上の「Show the Assistant Editor」をクリックし、エディタを2分割します。左側に Story Board、右側に ViewController.swift を表示します。

スクリーンショット 2015-11-01 1.13.47.png

Story Board 上の MapView を ViewController.swift で扱うために結びつけます。control キーを押しながら Map View をドラッグし、ViewController.swift で離します。

スクリーンショット 2015-11-01 1.12.37.png

「Name」は mapView とします。

スクリーンショット 2015-11-01 1.08.23.png

ソースコードが次のようになっていることを確認してください。

class ViewController: UIViewController {

    @IBOutlet weak var mapView: MKMapView!

    override func viewDidLoad() {

特定の位置を表示

表示したい位置を CLLocationCoordinate2DMake メソッドで指定します。今回はアップル本社周辺(緯度37.331652997806785, 経度-122.03072304117417)を表示してみます。

let coordinate = CLLocationCoordinate2DMake(37.331652997806785, -122.03072304117417)

表示する範囲を MKCoordinateSpanMake メソッドで指定します。数値が小さくなるほど表示される領域は狭くなります。(つまり、ズームレベルが高くなる)

let span = MKCoordinateSpanMake(0.01, 0.01)

最後にこの「位置」と「表示範囲」から Region オブジェクトを生成し、MapViewにセットします。

let region = MKCoordinateRegionMake(coordinate, span)
mapView.setRegion(region, animated:true)

これでアップル本社周辺が表示されたと思います。

Simulator Screen Shot 2015.11.01 2.13.23.png

まとめ

今回は地図を画面いっぱいに表示するところまで実装しました。

次回は地図上にピンを表示していきます。

blog.koogawa.com

Swift と MapKit で地図アプリを作成する(後編)

こちらの後編です。

blog.koogawa.com

前回は地図を画面いっぱいに表示するところまで実装しました。今回は地図上にピンを表示していきます。

ピンを表示する

地図上にピンを表示するためには MKPointAnnotation オブジェクトを使用します。このオブジェクトの coordinate プロパティにピンを表示する緯度経度を指定して、MapView に addAnnotation メソッドで追加します。

let annotation = MKPointAnnotation()
annotation.coordinate = CLLocationCoordinate2DMake(37.331652997806785, -122.03072304117417)
self.mapView.addAnnotation(annotation)

ピンが表示できました!

kobito.1446341336.196316.png

ピンのタイトルを指定する

これだけだとピンをタップしても何も起こりません。MKPointAnnotation クラスはプロパティに titlesubtitle を持っており、これに文字列を指定することで、ピンをタップした時にタイトルを表示するバルーンを出すことができます。

let annotation = MKPointAnnotation()
annotation.coordinate = CLLocationCoordinate2DMake(37.331652997806785, -122.03072304117417)
annotation.title = "title"
annotation.subtitle = "subtitle"
self.mapView.addAnnotation(annotation)

ピンをタップすると、バルーンが表示されると思います。

スクリーンショット 2015-11-01 10.41.58.png

ピンにアニメーションを付ける

ピンが上から降ってくるようにしてみます。

まずは MapView のデリゲートを ViewController.swift にセットします。Story Board で MapView を選択し、画面右側の「Connections Inspector」にある delegate の ○ と、画面左側の View Controller をドラッグアンドドロップで結びつけます。

kobito.1446342647.235789.png

次のようになっていれば delegate がセットできています。

kobito.1446343620.612640.png

次に、MapView のデリゲートメソッドを実装していきます。クラス宣言部に MKMapViewDelegate を追記します。

class ViewController: UIViewController, MKMapViewDelegate {

続いて viewForAnnotation デリゲートメソッドを実装します。このメソッドはピンが画面に表示する直前に呼ばれ、今から表示するピンの属性や外観などを MKPinAnnotationView オブジェクトを使って指定することができます。

func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
    if annotation is MKUserLocation {
        return nil
    }

    let reuseId = "pin"
    var pinView = mapView.dequeueReusableAnnotationViewWithIdentifier(reuseId) as? MKPinAnnotationView
    if pinView == nil {
        pinView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
        pinView?.animatesDrop = true
    }
    else {
        pinView?.annotation = annotation
    }
        
    return pinView
}

ピンは何度も再利用されます(UITableViewCell のようなイメージ)。最初に dequeueReusableAnnotationViewWithIdentifier で再利用できるピンを取り出します。再利用できるものがなければ新たに作成します。

アニメーションを付けるには animatesDrop プロパティに true を指定します。これでピンが上から降ってくるようになります。

demo.gif

ピンの色を変更する

ピンの色はデフォルトで赤ですが、MKPinAnnotationViewpinTintColor プロパティをセットすることで好きな色に変更できます。

pinView?.pinTintColor = UIColor.purpleColor()

kobito.1446344860.152417.png

バルーンにボタンを付けてみる

バルーンに UIButton を付けることもできます。まずは MKPinAnnotationViewcanShowCallout プロパティを true にセットします。これをやらないとバルーンが表示されないので注意してください。

pinView?.canShowCallout = true

次に、UIButton を生成して rightCalloutAccessoryView プロパティにセットします。

let rightButton: AnyObject! = UIButton(type: UIButtonType.DetailDisclosure)
pinView?.rightCalloutAccessoryView = rightButton as? UIView

これでバルーンにボタンが表示されました。

kobito.1446344956.686867.png

ボタンがタップされると calloutAccessoryControlTapped デリゲートが呼ばれます。ここに必要な処理を書いていくと良いでしょう。

func mapView(mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
    print(__FUNCTION__)
}

まとめ

今回は地図上にピンを表示するところまで実装しました。うまく動かない場合は、デリゲートがちゃんと指定されているか?デリゲートメソッドが呼ばれているか?を確認してみてください。

この記事内では割愛しましたが、地図上にはピンだけでなく 画像 を表示することもできます。また、ピンをドラッグして移動することもできます。興味のある方は私のサンプルコードを参照してください。

この記事が皆さまのお役に立てれば幸いです。