[iOS] MapKitの基本的な使い方(iOS9対応)
1 はじめに
MapKitFrameworkdは、地図を埋め込んビューを提供するものです。
機能として、地図にピンを置いたり、その注釈を表示したりすることができます。 iOS9以降は、さらに強化されて色々な機能が使えるようになっていますが、今回は、基本的な使用方法を改めて整理してみたいと思います。
2 セットアップ
MapKitを使用するためには、 General の Linked Frameworks and Libraries から MapKir.framework を追加し、ヘッダに <MapKit/MapKit.h> を追加する必要があります。
#import <UIKit/UIKit.h> #import <MapKit/MapKit.h> @interface ViewController : UIViewController<MKMapViewDelegate> @end
なお、上記では、ついでにMKMapViewDelegateも、ViewControllerに追加しちゃってます。
3 表示範囲
MapViewで何処の地図をどのような縮尺で表示するかは、MKCoordinateRegionで指定します。
MKCoordinateRegionとは、CLLocationCoordinate2D(経緯度)とMKCoordinateSpan(縮尺)を持った構造体です。
typedef struct { CLLocationCoordinate2D center; MKCoordinateSpan span; } MKCoordinateRegion;
ざっくり、使ってみると次のような感じです。
- (void)viewDidLoad { [super viewDidLoad]; MKCoordinateRegion cr = _mapView.region; // 東京スカイツリーの座標 cr.center = CLLocationCoordinate2DMake(35.710063, 139.8107); // 縮尺 0.01 cr.span = MKCoordinateSpanMake(0.01, 0.01); [_mapView setRegion:cr animated:NO]; }
(1) 座標
CLLocationCoordinate2Dは、double型の、longitude(経度)と latitude(緯度)を持つ構造体です。
typedef struct { CLLocationDegrees latitude; CLLocationDegrees longitude; } CLLocationCoordinate2D;
構造体生成用の関数は、次の通りです。
MKCoordinateSpan MKCoordinateSpanMake ( CLLocationDegrees latitudeDelta, CLLocationDegrees longitudeDelta );
なお、表示されている画面上の位置とMapView上で使用している経緯度への相互変換は次のメソッドを使用します。
// 経緯度を座標に変換 - (CGPoint)convertCoordinate:(CLLocationCoordinate2D)coordinate toPointToView:(NSView *)view // 座標を経緯度に変換 - (CLLocationCoordinate2D)convertPoint:(CGPoint)point toCoordinateFromView:(NSView *)view
(2) 縮尺
MKCoordinateSpanとは、latitudeDelta(緯度方向の表示範囲)と、longitudeDelta(経度方向の表示範囲)を持つ構造体です。 単位は度で、だいたい1度が約111kmぐらいです。
typedef struct { CLLocationDegrees latitudeDelta; CLLocationDegrees longitudeDelta; } MKCoordinateSpan;
構造体生成用の関数は、次の通りです。
MKCoordinateRegion MKCoordinateRegionMake ( CLLocationCoordinate2D centerCoordinate, MKCoordinateSpan span );
4 ピン
ちょっとややこしいのですが、MapViewでのピンは、「アノテーション」と「アノテーションビュー」という概念があります。 「アノテーション」は「モデル」であり地図上の位置を表現しています。そして、「アノテーションビュー」 の方は、その「モデル」表示する機能になります。
(1) MKPointAnnotation
単純にピンを立てるだけなら、MapViewに「アノテーション」を追加するだけです。 具体的には、addAnnotationメソッドでMKPointAnnotationを追加します。 MKPointAnnotationには、タイトルと詳細を設定するプロパティがあり、ピンをタップした時に、その内容が表示されます。
なお、この場合、「アノテーションビュー」は設定していませんので、デフォルトのビューで赤色ピンが表示されています。
// ピンを全て削除 [_mapView removeAnnotations: _mapView.annotations]; // 新しいピンを作成 MKPointAnnotation *annotation = [[MKPointAnnotation alloc] init]; annotation.coordinate = CLLocationCoordinate2DMake(43.065888, 141.354594); annotation.title = @"クラスメソッド(株) 札幌オフィス"; annotation.subtitle = @"札幌市中央区北3条西1丁目1-1"; // ピンを追加 [_mapView addAnnotation:annotation];
(2) MKPinAnnotationView
アノテーションビューを設定するには、WebViewのデリゲートメソッドであるmapView:viewForAnnotationを使用する必要があります。
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation
次の例では、同デリゲートでMKPinAnnotationViewを取得して、ピンの色をオレンジ色に設定してます。
ここで使用されているdequeueReusableAnnotationViewWithIdentifierは、再利用可能なアノテーションビューを検索するためのもので、再利用できるものが無い場合だけ生成するという処理になっています。
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id )annotation { // 識別子を定義 static NSString* Identifier = @"PinAnnotationIdentifier"; MKPinAnnotationView* pinView; // 再利用可能なものを取得 pinView = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:Identifier]; if (pinView == nil) { //取得できない場合だけ、新たに生成する pinView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:Identifier]; // ピンの色をオレンジ色にする pinView.pinTintColor = [UIColor orangeColor]; return pinView; } pinView.annotation = annotation; return pinView; }
色を自由に設定できるようになったのは、iOS9以降です。 iOS8までの場合、pinTintColorというプロパティは無く、pinColorで「赤」「緑」「紫」しか選択できないので注意が必要です。
// ピンの色 @property(strong, nonatomic) UIColor *pinTintColor
なお、MKPinAnnotationViewには、pinTintColorの他にも、次のようなプロパティもあります。
// 上から落ちてくるアニメーションをするかどうか @property(nonatomic) BOOL animatesDrop
(3) MKAnnotationView
先ほどのMKPinAnnotationViewは、ピン画像専用なのですが、これをMKAnnotationViewに変更すると、そのimageプロパティに設定した画像を表示できます。
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id )annotation { static NSString* Identifier = @"AnnotationIdentifier"; MKAnnotationView* pinView; // <= MKAnnotationViewを使用する pinView = (MKAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:Identifier]; if (pinView == nil) { pinView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:Identifier]; pinView.image = [UIImage imageNamed:@"player"]; // <= ピンの代わりに使用する画像 pinView.canShowCallout = true; } pinView.annotation = annotation; return pinView; }
※MKAnnotationViewでは、先に紹介した、落下アニメーションのanimatesDropは、使用できません。
5 Callout
(1) iOS8以前
MKPinAnnotationViewのcanShowCalloutをYESに設定することで、ピンをタップした時に吹き出しが表示できます。
titleへタイトル、そしてsubtitleに詳細情報が設定可能です。
MKPointAnnotation *annotation = [[MKPointAnnotation alloc] init]; annotation.coordinate = CLLocationCoordinate2DMake(43.065888, 141.354594); annotation.title = @"タイトル"; annotation.subtitle = @"詳細情報・・・";
また、MKPinAnnotationViewのleftCalloutAccessoryView及びrightCalloutAccessoryViewへもボタンなどが配置可能です。 なお、ここには、UIButtonに限らず、UIViewなら何でも置くことができます。
pinView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:Identifier]; // カレットを表示する pinView.canShowCallout = true; // 左にイメージビューを置く UIImage *image = [UIImage imageNamed:@"pin.png"]; UIImageView *imageView = [[UIImageView alloc] initWithImage:image]; pinView.leftCalloutAccessoryView = imageView; // 右にボタンを置く UIButton *rightButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure]; [rightButton setTitle:@"TEST" forState:UIControlStateNormal]; pinView.rightCalloutAccessoryView = rightButton;
(2) iOS9以前
iOS9以降、MKPinAnnotationViewにdetailCalloutAccessoryViewが追加されました。
このビューに設定することで、従来のsubtitleは表示されなくなり、ここにも自由なビューが置けることになったということです。
この辺を色々いじってみたものが、次のサンプルです。
- (void)viewDidLoad { [super viewDidLoad]; MKCoordinateRegion cr = _mapView.region; cr.center = CLLocationCoordinate2DMake(43.065888, 141.354594); cr.span = MKCoordinateSpanMake(0.01, 0.01);; [_mapView setRegion:cr animated:NO]; // 長押し検出 UILongPressGestureRecognizer *longPressGesture; longPressGesture = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPressGesture:)]; [_mapView addGestureRecognizer:longPressGesture]; _mapView.delegate = self; } - (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id )annotation { static NSString* Identifier = @"PinAnnotationIdentifier"; MKPinAnnotationView* pinView; pinView = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:Identifier]; if (pinView == nil) { pinView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:Identifier]; // カレットを表示する pinView.canShowCallout = true; // 上から落ちてくるアニメーション pinView.animatesDrop = true; // イメージビューの生成 UIImage *imageCubu = [UIImage imageNamed:@"cm.png"]; UIImageView *imageCubuView = [[UIImageView alloc] initWithImage:imageCubu]; // 詳細ビューにイメージビューを設定 pinView.detailCalloutAccessoryView = imageCubuView; } pinView.annotation = annotation; return pinView; } // 最初からアウトレットを表示状態にする - (void)mapView:(MKMapView *)aMapView didAddAnnotationViews:(NSArray *)views { [_mapView selectAnnotation:[_mapView.annotations lastObject] animated:YES]; }
そして、実行画面です。
6 MapType
最後に、MapTypeを変更した場合の、見え方の違いを紹介しておきます。
_mapView.mapType = MKMapTypeSatellite;
MapTypeには、次の5種類がありますが、どうも、手元では、新しく追加された2つは、何が違うのか上手く確認できませんでした。 何か、他にも必要なのかな・・・
- MKMapTypeStandard 標準の地図(デフォルト)
- MKMapTypeSatellite 航空写真
- MKMapTypeHybrid 標準の地図+航空写真
- MKMapTypeSatelliteFlyover 3Dビュー (iOS9以降)
- MKMapTypeHybridFlyover 3Dビュー (iOS9以降)
7 最後に
今回は、MapKitの基本的な利用方法について見てきました。 今後は、iOS9で追加された、新しい機能も積極的に使って見たいと思います。