這篇文章還是翻譯自raywenderlich,用Objective-C改寫了代碼。沒有逐字翻譯,如有錯漏,請指出。原文地址在這里。
1 概述
MapKit是iOS提供的一個很便捷的API,旨在幫助我們快速開發地理位置相關的應用。在這篇博客中涉及的地方叫Honolulu,是美國的一個城市,中文名是檀香山,是美國夏威夷州首府和港口城市。我第一次聽說檀香山應該是在歷史書上,似乎跟孫中山先生相關,這里暫時按下不表。
本文的例子中我們要添加一個地圖到APP中,然后通過經緯度定位到一個指定的位置,通過大頭針的形式來展示定位,點擊大頭針會顯示位置的一些信息。我們可以設置大頭針的顏色,當然我們更可以自定義圖片來替換默認的大頭針,豐富地圖內容。
2 開始
首先新建工程,命名為MapKitTutorial,然后添加一個Map Kit View到Storyboard中。先拖拽地圖視圖到充滿屏幕,然后選擇添加建議的約束即可,添加完約束效果如圖1所示。
然后需要在ViewController.m
中添加Map View的outlet關聯,通過CTRL+DRAG即可,命名為mapView,代碼如下,別忘記引入MapKit頭文件,注意要先導入MapKit庫(Xcode7.3里面,先選擇工程,然后在Capabilities那一項把Maps開關打開):
......
#import <MapKit/MapKit.h>
@interface ViewController ()
@property (weak, nonatomic) IBOutlet MKMapView *mapView;
@end
這樣,我們編譯運行項目,可以看到地圖出現了,默認顯示的是中國地圖在中央位置,這個應該是跟系統環境相關。如圖2所示:
3 設置可視區域
接下來重點來了,我們要設置一個可視區域。學過地理
的都知道(我是地理盲),一個地理位置我們是通過緯度和經度來確定一個位置,緯度中北緯和南緯各分為90度,經度西經和東經則各為180度。在iOS開發中,北緯和東經我們用正數表示,南緯和西經用負數表示。
接下來我們要設置一個可視區域,不設置我們看到的只是一個默認區域,如前面看到的一樣。設置可視區域的代碼如下,然后在方法viewDidLoad中加入調用:
- (void)centerMapOnLocation {
//1 設置好緯度和經度
CLLocationCoordinate2D initialLocation = {21.282778, -157.829444};
CLLocationDistance regionRadius = 1000;
MKCoordinateRegion coordinateRegion = MKCoordinateRegionMakeWithDistance(initialLocation, regionRadius * 2, regionRadius * 2);
[self.mapView setRegion:coordinateRegion];
}
這里來解析一下,首先是設置好中心點的經緯度。然后是設置區域半徑,包括橫向和縱向半徑,這里都設置的為2000米(南北和東西的跨度范圍)。接著根據中心點經緯度和區域半徑創建一個Region,最后調用MapView的setRegion方法即可。這時我們再運行代碼可以看到效果如圖3所示,我們看到與前面不同了,確實地圖已經定位到我們設置的中心點和區域大小了:
4 設置標記
接下來來設置一個標記,也就是通常見到的那種大頭針。先新建一個類Artwork來表示標記內容,包括位置坐標,標題和副標題等。位置坐標用于確定大頭針位置,而標題和副標題則是點擊大頭針的時候顯示的內容。
@interface Artwork : NSObject <MKAnnotation>
@property (nonatomic, copy) NSString *title;
@property (nonatomic, copy) NSString *subtitle;
@property (nonatomic, strong) NSString *locationName;
@property (nonatomic, strong) NSString *discipline;
@property (nonatomic) CLLocationCoordinate2D coordinate;
- (instancetype)init:(NSString *)title
locationName:(NSString *)locationName
discipline:(NSString *)discipline
coordinate:(CLLocationCoordinate2D)coordinate;
@end
類Artwork定義如上所示,然后在ViewController.m中加入添加大頭針的代碼:
- (void)addAnnotation {
CLLocationCoordinate2D coordinate = {21.283921, -157.831661};
Artwork *artwork = [[Artwork alloc] init: @"King David Kalakaua"
locationName: @"Waikiki Gateway Park"
discipline: @"Sculpture"
coordinate: coordinate];
[self.mapView addAnnotation:artwork];
}
注意這里的坐標暫時是自己手動指定,在后面我們會從一個JSON文件中解析一系列的坐標位置然后設置多個大頭針。然后里面的locationName其實是位置名字,而MKAnnotation是一個協議,地圖標記的協議,它里面主要定義了一系列的屬性值(這里可以發現協議不只有方法,也可以定義屬性的),就是標記必須的幾個屬性title,subtitle,coordinate。
然后在viewDidLoad中加入該方法的調用,可以看到大頭針顯示,點擊可以看到title和subtitle的顯示。
僅僅這樣當然是不夠的,為了APP的個性化,我們希望可以自定義大頭針顯示方式,可以通過設置ViewController實現協議MKMapViewDelegate
,然后將mapView的delegate設置為ViewController,并在ViewController中實現協議的viewForAnnotation方法即可。代碼如下:
- (void)viewDidLoad {
[super viewDidLoad];
[self centerMapOnLocation];
[self addAnnotation];
//1新增delegate設置
self.mapView.delegate = self;
}
//2 新增代理方法實現
#pragma MKMapViewDelegate
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation {
if ([annotation isKindOfClass:[Artwork class]]) {
NSString *identifier = @"pin";
MKPinAnnotationView *view = (MKPinAnnotationView *)[self.mapView dequeueReusableAnnotationViewWithIdentifier:identifier];
if (!view) {
view = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:identifier];
//自定義后需要設置canShowCallout為YES,不然點擊不會顯示信息。
view.canShowCallout = YES;
//設置信息展示偏移
view.calloutOffset = CGPointMake(-10, 5);
//設置信息按鈕
view.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
}
//設置大頭針顏色
view.pinTintColor = [UIColor blueColor];
return view;
}
return nil;
}
方法viewForAnnotation中我們定義了大頭針的信息偏移和在顯示內容中添加了一個信息按鈕。偏移位置是針對大頭針的中間頂部而言的,這里我們設置信息展示的X軸向左偏移中間位置10個Point,Y軸則從頂部往下移動5個Point,并將大頭針的顏色設置為藍色,運行效果如圖5所示。
5 啟動地圖APP
上一節點擊信息按鈕,并沒有反應,接下來就要加入點擊信息按鈕的事件響應,這里我們是跳轉到地圖app中,顯示從當前位置到公園的駕駛路線。
首先需要在類Artwork中加入一個方法,用來創建MapItem。這里需要導入Contacts頭文件(原文中用的是Address頭文件,iOS9以后的系統中已經不用Address,所以我這里用Contacts來替代)。
#import <Contacts/Contacts.h>
- (MKMapItem *)mapItem {
NSDictionary *addressDictionary = @{CNPostalAddressStreetKey: self.locationName};
MKPlacemark *placemark = [[MKPlacemark alloc] initWithCoordinate:self.coordinate addressDictionary:addressDictionary];
MKMapItem *mapItem = [[MKMapItem alloc] initWithPlacemark:placemark];
return mapItem;
}
然后在ViewController中實現另外一個協議方法如下。另外,在Xcode菜單欄的Product\Scheme\Edit Scheme
選擇Run的Options選項,設置好默認位置為Honolulu,如圖6所示。這樣點擊右側的信息按鈕,就會跳轉到地圖APP中了(這里可能是由于高德地圖問題,顯示不了駕駛路線,暫時沒有找到解決方法,若有知道的,麻煩告知一聲)。
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control {
Artwork *artwork = (Artwork *)view.annotation;
NSDictionary *launchOptions = @{MKLaunchOptionsDirectionsModeKey:MKLaunchOptionsDirectionsModeDriving};
[[artwork mapItem] openInMapsWithLaunchOptions:launchOptions];
}
運行效果如下:
6 用戶位置授權
在iOS應用中,如果應用要請求位置信息,一般是需要提示用戶是否授權的,這個功能怎么實現呢?首先在ViewController中加入CLLocationManager,然后在viewDidAppear中加入請求授權的函數調用,如果一次請求授權允許了,系統會記錄授權的狀態,下次啟動應用就不需要再次授權了。如果拒絕授權了,以后要開啟只能到系統設置里面開啟。
注意:一般請求授權分為兩種方式,一種是requestWhenInUseAuthorization表示只有應用在前臺允許的時候獲取位置信息,而另外一種是requestAlwaysAuthorization表示只要應用在運行就可以獲取位置信息,不管在前臺還是后臺運行,為了不泄露隱私,蘋果官方是建議用第一種,即在前臺運行的時候運行訪問位置信息。
另外一個容易忽視的一點,就是還需要在項目的Info.plist中加入一個配置。配置鍵名為NSLocationWhenInUseUsageDescription
,內容為請求授權的文字信息,本項目填的信息為To show you cool things nearby
。
@property (strong, nonatomic) CLLocationManager *locationManager;
- (void)checkLocationStatus {
if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusAuthorizedWhenInUse) {
self.mapView.showsUserLocation = YES;
} else {
[self.locationManager requestWhenInUseAuthorization];
}
}
這樣在啟動應用的時候,就會彈出一個提示框,內容為應用請求訪問您的位置信息,讓你決定是否授權。效果如圖8所示:
7 其他
原文中還有一節是通過一個JSON文件來設置多個大頭針,并根據位置信息不同設置不同的大頭針的顏色,與設置一個大頭針效果類似,只是多了JSON解析的步驟,這里不再贅述,可以參見我的項目最終代碼。
另外,地圖里面還可以設置覆蓋層(overlay),比如用圖片來設置覆蓋層,或者設置路徑,多邊形等,可以參見這篇文章,這篇文章代碼的Objective-C版本地址參見這里