這篇文章還是翻譯自raywenderlich宋下,用Objective-C改寫了代碼。沒有逐字翻譯渡蜻,如有錯(cuò)漏术吝,請(qǐng)指出。原文地址在這里茸苇。
1 概述
MapKit是iOS提供的一個(gè)很便捷的API排苍,旨在幫助我們快速開發(fā)地理位置相關(guān)的應(yīng)用。在這篇博客中涉及的地方叫Honolulu学密,是美國(guó)的一個(gè)城市淘衙,中文名是檀香山,是美國(guó)夏威夷州首府和港口城市腻暮。我第一次聽說檀香山應(yīng)該是在歷史書上彤守,似乎跟孫中山先生相關(guān)毯侦,這里暫時(shí)按下不表。
本文的例子中我們要添加一個(gè)地圖到APP中具垫,然后通過經(jīng)緯度定位到一個(gè)指定的位置侈离,通過大頭針的形式來展示定位,點(diǎn)擊大頭針會(huì)顯示位置的一些信息做修。我們可以設(shè)置大頭針的顏色霍狰,當(dāng)然我們更可以自定義圖片來替換默認(rèn)的大頭針抡草,豐富地圖內(nèi)容饰及。
2 開始
首先新建工程,命名為MapKitTutorial康震,然后添加一個(gè)Map Kit View到Storyboard中燎含。先拖拽地圖視圖到充滿屏幕,然后選擇添加建議的約束即可腿短,添加完約束效果如圖1所示屏箍。
然后需要在ViewController.m
中添加Map View的outlet關(guān)聯(lián),通過CTRL+DRAG即可橘忱,命名為mapView赴魁,代碼如下,別忘記引入MapKit頭文件钝诚,注意要先導(dǎo)入MapKit庫(Xcode7.3里面颖御,先選擇工程,然后在Capabilities那一項(xiàng)把Maps開關(guān)打開):
......
#import <MapKit/MapKit.h>
@interface ViewController ()
@property (weak, nonatomic) IBOutlet MKMapView *mapView;
@end
這樣凝颇,我們編譯運(yùn)行項(xiàng)目潘拱,可以看到地圖出現(xiàn)了,默認(rèn)顯示的是中國(guó)地圖在中央位置拧略,這個(gè)應(yīng)該是跟系統(tǒng)環(huán)境相關(guān)芦岂。如圖2所示:
3 設(shè)置可視區(qū)域
接下來重點(diǎn)來了,我們要設(shè)置一個(gè)可視區(qū)域垫蛆。學(xué)過地理
的都知道(我是地理盲)禽最,一個(gè)地理位置我們是通過緯度和經(jīng)度來確定一個(gè)位置,緯度中北緯和南緯各分為90度袱饭,經(jīng)度西經(jīng)和東經(jīng)則各為180度弛随。在iOS開發(fā)中,北緯和東經(jīng)我們用正數(shù)表示宁赤,南緯和西經(jīng)用負(fù)數(shù)表示舀透。
接下來我們要設(shè)置一個(gè)可視區(qū)域,不設(shè)置我們看到的只是一個(gè)默認(rèn)區(qū)域决左,如前面看到的一樣愕够。設(shè)置可視區(qū)域的代碼如下走贪,然后在方法viewDidLoad中加入調(diào)用:
- (void)centerMapOnLocation {
//1 設(shè)置好緯度和經(jīng)度
CLLocationCoordinate2D initialLocation = {21.282778, -157.829444};
CLLocationDistance regionRadius = 1000;
MKCoordinateRegion coordinateRegion = MKCoordinateRegionMakeWithDistance(initialLocation, regionRadius * 2, regionRadius * 2);
[self.mapView setRegion:coordinateRegion];
}
這里來解析一下,首先是設(shè)置好中心點(diǎn)的經(jīng)緯度惑芭。然后是設(shè)置區(qū)域半徑坠狡,包括橫向和縱向半徑,這里都設(shè)置的為2000米(南北和東西的跨度范圍)遂跟。接著根據(jù)中心點(diǎn)經(jīng)緯度和區(qū)域半徑創(chuàng)建一個(gè)Region逃沿,最后調(diào)用MapView的setRegion方法即可。這時(shí)我們?cè)龠\(yùn)行代碼可以看到效果如圖3所示幻锁,我們看到與前面不同了凯亮,確實(shí)地圖已經(jīng)定位到我們?cè)O(shè)置的中心點(diǎn)和區(qū)域大小了:
4 設(shè)置標(biāo)記
接下來來設(shè)置一個(gè)標(biāo)記,也就是通常見到的那種大頭針哄尔。先新建一個(gè)類Artwork來表示標(biāo)記內(nèi)容假消,包括位置坐標(biāo),標(biāo)題和副標(biāo)題等岭接。位置坐標(biāo)用于確定大頭針位置富拗,而標(biāo)題和副標(biāo)題則是點(diǎn)擊大頭針的時(shí)候顯示的內(nèi)容。
@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];
}
注意這里的坐標(biāo)暫時(shí)是自己手動(dòng)指定啃沪,在后面我們會(huì)從一個(gè)JSON文件中解析一系列的坐標(biāo)位置然后設(shè)置多個(gè)大頭針。然后里面的locationName其實(shí)是位置名字窄锅,而MKAnnotation是一個(gè)協(xié)議创千,地圖標(biāo)記的協(xié)議,它里面主要定義了一系列的屬性值(這里可以發(fā)現(xiàn)協(xié)議不只有方法酬滤,也可以定義屬性的)签餐,就是標(biāo)記必須的幾個(gè)屬性title,subtitle盯串,coordinate氯檐。
然后在viewDidLoad中加入該方法的調(diào)用,可以看到大頭針顯示体捏,點(diǎn)擊可以看到title和subtitle的顯示冠摄。
僅僅這樣當(dāng)然是不夠的,為了APP的個(gè)性化几缭,我們希望可以自定義大頭針顯示方式河泳,可以通過設(shè)置ViewController實(shí)現(xiàn)協(xié)議MKMapViewDelegate
,然后將mapView的delegate設(shè)置為ViewController年栓,并在ViewController中實(shí)現(xiàn)協(xié)議的viewForAnnotation方法即可拆挥。代碼如下:
- (void)viewDidLoad {
[super viewDidLoad];
[self centerMapOnLocation];
[self addAnnotation];
//1新增delegate設(shè)置
self.mapView.delegate = self;
}
//2 新增代理方法實(shí)現(xiàn)
#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];
//自定義后需要設(shè)置canShowCallout為YES,不然點(diǎn)擊不會(huì)顯示信息某抓。
view.canShowCallout = YES;
//設(shè)置信息展示偏移
view.calloutOffset = CGPointMake(-10, 5);
//設(shè)置信息按鈕
view.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
}
//設(shè)置大頭針顏色
view.pinTintColor = [UIColor blueColor];
return view;
}
return nil;
}
方法viewForAnnotation中我們定義了大頭針的信息偏移和在顯示內(nèi)容中添加了一個(gè)信息按鈕纸兔。偏移位置是針對(duì)大頭針的中間頂部而言的惰瓜,這里我們?cè)O(shè)置信息展示的X軸向左偏移中間位置10個(gè)Point,Y軸則從頂部往下移動(dòng)5個(gè)Point汉矿,并將大頭針的顏色設(shè)置為藍(lán)色崎坊,運(yùn)行效果如圖5所示。
5 啟動(dòng)地圖APP
上一節(jié)點(diǎn)擊信息按鈕洲拇,并沒有反應(yīng)奈揍,接下來就要加入點(diǎn)擊信息按鈕的事件響應(yīng)爆哑,這里我們是跳轉(zhuǎn)到地圖app中今野,顯示從當(dāng)前位置到公園的駕駛路線。
首先需要在類Artwork中加入一個(gè)方法湿硝,用來創(chuàng)建MapItem蚕捉。這里需要導(dǎo)入Contacts頭文件(原文中用的是Address頭文件奏篙,iOS9以后的系統(tǒng)中已經(jīng)不用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中實(shí)現(xiàn)另外一個(gè)協(xié)議方法如下。另外为严,在Xcode菜單欄的Product\Scheme\Edit Scheme
選擇Run的Options選項(xiàng)敛熬,設(shè)置好默認(rèn)位置為Honolulu,如圖6所示第股。這樣點(diǎn)擊右側(cè)的信息按鈕应民,就會(huì)跳轉(zhuǎn)到地圖APP中了(這里可能是由于高德地圖問題,顯示不了駕駛路線夕吻,暫時(shí)沒有找到解決方法诲锹,若有知道的,麻煩告知一聲)涉馅。
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control {
Artwork *artwork = (Artwork *)view.annotation;
NSDictionary *launchOptions = @{MKLaunchOptionsDirectionsModeKey:MKLaunchOptionsDirectionsModeDriving};
[[artwork mapItem] openInMapsWithLaunchOptions:launchOptions];
}
運(yùn)行效果如下:
6 用戶位置授權(quán)
在iOS應(yīng)用中归园,如果應(yīng)用要請(qǐng)求位置信息,一般是需要提示用戶是否授權(quán)的稚矿,這個(gè)功能怎么實(shí)現(xiàn)呢庸诱?首先在ViewController中加入CLLocationManager,然后在viewDidAppear中加入請(qǐng)求授權(quán)的函數(shù)調(diào)用晤揣,如果一次請(qǐng)求授權(quán)允許了桥爽,系統(tǒng)會(huì)記錄授權(quán)的狀態(tài),下次啟動(dòng)應(yīng)用就不需要再次授權(quán)了昧识。如果拒絕授權(quán)了钠四,以后要開啟只能到系統(tǒng)設(shè)置里面開啟。
注意:一般請(qǐng)求授權(quán)分為兩種方式跪楞,一種是requestWhenInUseAuthorization表示只有應(yīng)用在前臺(tái)允許的時(shí)候獲取位置信息缀去,而另外一種是requestAlwaysAuthorization表示只要應(yīng)用在運(yùn)行就可以獲取位置信息环疼,不管在前臺(tái)還是后臺(tái)運(yùn)行,為了不泄露隱私朵耕,蘋果官方是建議用第一種炫隶,即在前臺(tái)運(yùn)行的時(shí)候運(yùn)行訪問位置信息。
另外一個(gè)容易忽視的一點(diǎn)阎曹,就是還需要在項(xiàng)目的Info.plist中加入一個(gè)配置伪阶。配置鍵名為NSLocationWhenInUseUsageDescription
,內(nèi)容為請(qǐng)求授權(quán)的文字信息处嫌,本項(xiàng)目填的信息為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];
}
}
這樣在啟動(dòng)應(yīng)用的時(shí)候,就會(huì)彈出一個(gè)提示框熏迹,內(nèi)容為應(yīng)用請(qǐng)求訪問您的位置信息檐薯,讓你決定是否授權(quán)。效果如圖8所示:
7 其他
原文中還有一節(jié)是通過一個(gè)JSON文件來設(shè)置多個(gè)大頭針注暗,并根據(jù)位置信息不同設(shè)置不同的大頭針的顏色坛缕,與設(shè)置一個(gè)大頭針效果類似,只是多了JSON解析的步驟捆昏,這里不再贅述赚楚,可以參見我的項(xiàng)目最終代碼。
另外骗卜,地圖里面還可以設(shè)置覆蓋層(overlay)宠页,比如用圖片來設(shè)置覆蓋層,或者設(shè)置路徑寇仓,多邊形等举户,可以參見這篇文章,這篇文章代碼的Objective-C版本地址參見這里