一. 地圖的基本使用
1. 設(shè)置地圖顯示類型
- 地圖的樣式可以手動(dòng)設(shè)置, 在iOS9.0之前有3種, iOS9.0之后增加了2種
- 設(shè)置方式
self.mapView.mapType = MKMapTypeStandard;
| 枚舉類型 | 對(duì)應(yīng)含義 |
| ------------ | ------------- |
| MKMapTypeStandard | 標(biāo)準(zhǔn)地圖 |
| MKMapTypeSatellite | 衛(wèi)星地圖 |
| MKMapTypeHybrid | 混合模式(標(biāo)準(zhǔn)+衛(wèi)星) |
| MKMapTypeSatelliteFlyover | 3D立體衛(wèi)星(iOS9.0) |
| MKMapTypeHybridFlyover | 3D立體混合(iOS9.0) |
2. 設(shè)置地圖控制項(xiàng)
- 地圖的旋轉(zhuǎn), 縮放, 移動(dòng)等等操作行為都可以開啟或者關(guān)閉
- 設(shè)置方式
self.customMapView.zoomEnabled = YES; // 是否縮放
self.customMapView.scrollEnabled = YES; // 是否滾動(dòng)
self.customMapView.rotateEnabled = YES; // 是否旋轉(zhuǎn)
self.customMapView.pitchEnabled = NO; // 是否顯示3DVIEW
3. 設(shè)置地圖顯示項(xiàng)
- 地圖上的指南針, 比例尺, 建筑物, POI點(diǎn)都可以控制是否顯示
- 設(shè)置方式
self.customMapView.showsCompass = YES; // 是否顯示指南針
self.customMapView.showsScale = YES; // 是否顯示比例尺
self.customMapView.showsTraffic = YES; // 是否顯示交通
self.customMapView.showsBuildings = YES; // 是否顯示建筑物
4. 顯示用戶位置
- 可以設(shè)置顯示用戶當(dāng)前所在位置, 以一個(gè)藍(lán)點(diǎn)的形式呈現(xiàn)在地圖上
- 設(shè)置方式
方案1:
self.customMapView.showsUserLocation = YES;
效果:
會(huì)在地圖上顯示一個(gè)藍(lán)點(diǎn), 標(biāo)識(shí)用戶所在位置; 但地圖不會(huì)縮放, 而且當(dāng)用戶位置移動(dòng)時(shí), 地圖不會(huì)跟隨用戶位置移動(dòng)而移動(dòng)
方案2:
self.customMapView.userTrackingMode = MKUserTrackingModeFollowWithHeading;
效果:
會(huì)在地圖上顯示一個(gè)藍(lán)點(diǎn), 標(biāo)識(shí)用戶所在位置; 而且地圖縮放到合適比例,顯示用戶位置, 當(dāng)用戶位置移動(dòng)時(shí), 地圖會(huì)跟隨用戶位置移動(dòng)而移動(dòng); 但是有時(shí)候失效;
注意事項(xiàng): 如果要顯示用戶位置, 在iOS8.0之后, 需要主動(dòng)請(qǐng)求用戶授權(quán)
5. 測(cè)試環(huán)境
- 加載地圖數(shù)據(jù)需要聯(lián)網(wǎng)
- XCode版本根據(jù)測(cè)試選擇不同版本(iOS9.0 只能使用 XCode7.0版本)
- iOS系統(tǒng)版本根據(jù)測(cè)試選擇不同版本(例如地圖類型, 在iOS9.0之后才有新增)
6. 常見問題總結(jié)
- 地圖加載不顯示?
檢查網(wǎng)絡(luò)是否通暢 - 地圖放的太大都是格子, 禁止瀏覽
正常, 為了安全等原因, 不會(huì)看的太詳細(xì) - 地圖運(yùn)行起來APP占用內(nèi)存非常大
正常, 地圖加載了很多資源 - 用戶位置不顯示
首先, 檢查代碼, 是否有設(shè)置顯示用戶位置,是否有進(jìn)行請(qǐng)求位置授權(quán)
其次, 查看模擬器是否有位置信息
第三, 重置模擬器, 模擬器又發(fā)神經(jīng)了.
二. 地圖的中級(jí)使用
1. 查看當(dāng)前用戶位置信息
- 設(shè)置地圖代理
- 實(shí)現(xiàn)代理方法
-(void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation
{
NSLog(@"%@", userLocation);
}
2. 調(diào)整地圖顯示中心
- 確定地圖中心經(jīng)緯度坐標(biāo)
CLLocationCoordinate2D center = CLLocationCoordinate2DMake(21.123, 121.345);
- 設(shè)置地圖中心為給定的經(jīng)緯度坐標(biāo)
[mapView setCenterCoordinate:center animated:YES];
3. 調(diào)整地圖顯示區(qū)域
- 獲取合適的區(qū)域跨度
實(shí)現(xiàn)當(dāng)?shù)貓D區(qū)域發(fā)生改變時(shí)調(diào)用的代理代理方法, 并調(diào)整地圖區(qū)域到合適比例, 并在對(duì)應(yīng)的方法中, 獲取對(duì)應(yīng)的跨度信息
- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated
{
NSLog(@"%f---%f", mapView.region.span.latitudeDelta, mapView.region.span.longitudeDelta);
}
- 創(chuàng)建一個(gè)區(qū)域(包含區(qū)域中心, 和區(qū)域跨度)
CLLocationCoordinate2D center = CLLocationCoordinate2DMake(21.123, 121.345);
MKCoordinateSpan span = MKCoordinateSpanMake(0.1, 0.1);
MKCoordinateRegion region = MKCoordinateRegionMake(center, span);
- 設(shè)置地圖顯示區(qū)域
[self.mapView setRegion:region animated:YES];
- 概念解釋
MKCoordinateSpan 跨度解釋:
latitudeDelta:緯度跨度肛响,因?yàn)槟媳本暩?0.0度,所以此值的范圍是(0.0---180.0);此值表示打毛,整個(gè)地圖視圖寬度,顯示多大跨度;
longitudeDelta:經(jīng)度跨度,因?yàn)闁|西經(jīng)各180.0度,所以此值范圍是(0.0---360.0):此值表示镊尺,整個(gè)地圖視圖高度,顯示多大跨度;
注意:地圖視圖顯示并思,不會(huì)更改地圖的比例庐氮,會(huì)以地圖視圖高度或?qū)挾容^小的那個(gè)為基準(zhǔn),按比例調(diào)整
4. MKUserLocation 大頭針數(shù)據(jù)模型詳解
MKUserLocation : 被稱作“大頭針(數(shù)據(jù))模型”;
其實(shí)喊什么都行宋彼,本質(zhì)就是一個(gè)數(shù)據(jù)模型旭愧,只不過此模型遵循了大頭針要遵循的協(xié)議(MKAnnotation)
重要屬性:
location : 用戶當(dāng)前所在位置信息(CLLocation對(duì)象)
title : 大頭針標(biāo)注要顯示的標(biāo)題(NSString對(duì)象)
subtitle : 大頭針標(biāo)注要顯示的子標(biāo)題(NSString對(duì)象)
5. 測(cè)試環(huán)境
- 加載地圖數(shù)據(jù)需要聯(lián)網(wǎng)
- XCode版本不限
- iOS系統(tǒng)版本不限
6. 常見問題總結(jié)
地圖上的藍(lán)點(diǎn)為啥不顯示?
第一: 確定代碼是否有誤(例如, 是否顯示了用戶位置)
第二: 確定模擬器是否設(shè)置位置
第三: 看下位置在哪, 是不是不在當(dāng)前地圖顯示區(qū)域地圖跨度設(shè)置之后, 最終顯示的跨度和設(shè)置數(shù)值不一致?
因?yàn)榈厍虻牟皇钦叫蔚? 隨著用戶的位置移動(dòng), 會(huì)自動(dòng)修正地圖跨度, 保持地圖不變形;
三. 地圖高級(jí)-大頭針基本使用
1. 理論支撐(必須掌握)
按照MVC的原則
在地圖上操作大頭針,實(shí)際上是控制大頭針數(shù)據(jù)模型
- 添加大頭針就是添加大頭針數(shù)據(jù)模型
- 刪除大頭針就是刪除大頭針數(shù)據(jù)模型
2. 在地圖上添加大頭針視圖
- 自定義大頭針數(shù)據(jù)模型
- 創(chuàng)建繼承自NSObject的數(shù)據(jù)模型XMGAnnotation, 遵循大頭針數(shù)據(jù)模型必須遵循的協(xié)議(MKAnnotation)
- 注意將協(xié)議@property 中的readonly 去掉;
- 創(chuàng)建大頭針數(shù)據(jù)模型, 并初始化參數(shù)
XMGAnnotation *annotation = [[XMGAnnotation alloc] init];
annotation.coordinate = coordinate;
annotation.title = @"南山";
annotation.subtitle = @"靈芝";
- 調(diào)用地圖的添加大頭針數(shù)據(jù)模型方法
[self.customMapView addAnnotation:annotation];
3. 移除大頭針(所有大頭針)
NSArray *annotations = self.customMapView.annotations;
[self.customMapView removeAnnotations:annotations];
4. 測(cè)試環(huán)境
- 加載地圖數(shù)據(jù)需要聯(lián)網(wǎng)
- XCode版本不限
- iOS系統(tǒng)版本不限
四. 地圖高級(jí)-大頭針的自定義
1. 理論支撐
按照MVC的原則
- 每當(dāng)添加一個(gè)大頭針數(shù)據(jù)模型時(shí), 地圖就會(huì)調(diào)用對(duì)應(yīng)的代理方法, 查找對(duì)應(yīng)的大頭針視圖,顯示在地圖上;
- 如果該方法沒有實(shí)現(xiàn), 或者返回nil, 那么就會(huì)使用系統(tǒng)默認(rèn)的大頭針視圖
2. 模擬系統(tǒng)默認(rèn)的大頭針實(shí)現(xiàn)方案
- 實(shí)現(xiàn)當(dāng)添加大頭針數(shù)據(jù)模型時(shí),地圖回調(diào)的代理方法
-(MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(XMGAnnotation *)annotation
{
}
實(shí)現(xiàn)須知
大頭針系統(tǒng)對(duì)應(yīng)的視圖是 MKPinAnnotationView,它繼承自 MKAnnotationView
地圖上的大頭針視圖宙暇,和tableview上的cell一樣输枯,都使用“循環(huán)利用”的機(jī)制
實(shí)現(xiàn)代碼
static NSString *pinID = @"pinID";
MKPinAnnotationView *pinView = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:pinID];
if (!pinView) {
pinView = [[MKPinAnnotationView alloc] initWithAnnotation:nil reuseIdentifier:pinID];
}
pinView.annotation = annotation;
// 彈出標(biāo)注
pinView.canShowCallout = YES;
// 修改大頭針顏色
pinView.pinColor = MKPinAnnotationColorPurple;
// 設(shè)置大頭針從天而降
pinView.animatesDrop = YES;
// 設(shè)置大頭針可以被拖拽(父類中的屬性)
pinView.draggable = YES;
return pinView;
3. 自定義大頭針
- 實(shí)現(xiàn)當(dāng)添加大頭針數(shù)據(jù)模型時(shí),地圖回調(diào)的代理方法
-(MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(XMGAnnotation *)annotation
{
}
實(shí)現(xiàn)須知
如果想要自定義大頭針, 必須使用 MKAnnotationView 或者 自定義的子類
但是不能直接使用系統(tǒng)默認(rèn)的大頭針, 會(huì)無效
實(shí)現(xiàn)代碼
// 自定義大頭針
static NSString *pinID = @"pinID";
MKAnnotationView *customPinView = [mapView dequeueReusableAnnotationViewWithIdentifier:pinID];
if (!customPinView) {
customPinView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:pinID];
}
// 設(shè)置大頭針圖片
customPinView.image = [UIImage imageNamed:@"category_3"];
// 設(shè)置大頭針可以彈出標(biāo)注
customPinView.canShowCallout = YES;
// 設(shè)置標(biāo)注左側(cè)視圖
UIImageView *leftIV = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 40, 40)];
leftIV.image = [UIImage imageNamed:@"huba.jpeg"];
customPinView.leftCalloutAccessoryView = leftIV;
// 設(shè)置標(biāo)注右側(cè)視圖
UIImageView *rightIV = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 40, 40)];
rightIV.image = [UIImage imageNamed:@"eason.jpg"];
customPinView.rightCalloutAccessoryView = rightIV;
// 設(shè)置標(biāo)注詳情視圖(iOS9.0)
customPinView.detailCalloutAccessoryView = [[UISwitch alloc] init];
return customPinView;
4. 代理方法補(bǔ)充
- 選中一個(gè)大頭針時(shí)調(diào)用
-(void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view
{
NSLog(@"選中%@", [view.annotation title]);
}
- 取消選中大頭針時(shí)調(diào)用
-(void)mapView:(MKMapView *)mapView didDeselectAnnotationView:(MKAnnotationView *)view
{
NSLog(@"取消選中%@", [view.annotation title]);
}
5. 測(cè)試環(huán)境
- 加載地圖數(shù)據(jù)需要聯(lián)網(wǎng)
- XCode版本不限
- iOS系統(tǒng)版本不限
6. 常見問題總結(jié)
- 代碼運(yùn)行在低版本的XCode上, 編譯失敗
第一: 語法錯(cuò)誤; XCode7.0 對(duì)于OC語法優(yōu)化了一些, 需要手動(dòng)調(diào)整
第二: iOS9.0的SDK, 在XCode7.0之前的版本沒有對(duì)應(yīng)的API
五. 利用系統(tǒng)App導(dǎo)航
1. 導(dǎo)航的三種實(shí)現(xiàn)方案
- 可以將需要導(dǎo)航的位置丟給系統(tǒng)的地圖APP進(jìn)行導(dǎo)航
- 發(fā)送網(wǎng)絡(luò)請(qǐng)求到公司服務(wù)器獲取導(dǎo)航數(shù)據(jù), 然后自己手動(dòng)繪制導(dǎo)航
- 利用三方SDK實(shí)現(xiàn)導(dǎo)航(百度)
2. 直接將起點(diǎn)和終點(diǎn), 傳遞給系統(tǒng)地圖, 利用系統(tǒng)APP, 進(jìn)行導(dǎo)航
- 利用"反推法", 記住關(guān)鍵代碼即可
- 代碼如下:
// 根據(jù)兩個(gè)地標(biāo)對(duì)象進(jìn)行調(diào)用系統(tǒng)導(dǎo)航
- (void)beginNavWithBeginPlacemark:(CLPlacemark *)beginPlacemark andEndPlacemark:(CLPlacemark *)endPlacemark
{
// 創(chuàng)建起點(diǎn):根據(jù) CLPlacemark 地標(biāo)對(duì)象創(chuàng)建 MKPlacemark 地標(biāo)對(duì)象
MKPlacemark *itemP1 = [[MKPlacemark alloc] initWithPlacemark:beginPlacemark];
MKMapItem *item1 = [[MKMapItem alloc] initWithPlacemark:itemP1];
// 創(chuàng)建終點(diǎn):根據(jù) CLPlacemark 地標(biāo)對(duì)象創(chuàng)建 MKPlacemark 地標(biāo)對(duì)象
MKPlacemark *itemP2 = [[MKPlacemark alloc] initWithPlacemark:endPlacemark];
MKMapItem *item2 = [[MKMapItem alloc] initWithPlacemark:itemP2];
NSDictionary *launchDic = @{
// 設(shè)置導(dǎo)航模式參數(shù)
MKLaunchOptionsDirectionsModeKey : MKLaunchOptionsDirectionsModeDriving,
// 設(shè)置地圖類型
MKLaunchOptionsMapTypeKey : @(MKMapTypeHybridFlyover),
// 設(shè)置是否顯示交通
MKLaunchOptionsShowsTrafficKey : @(YES),
};
// 根據(jù) MKMapItem 數(shù)組 和 啟動(dòng)參數(shù)字典 來調(diào)用系統(tǒng)地圖進(jìn)行導(dǎo)航
[MKMapItem openMapsWithItems:@[item1, item2] launchOptions:launchDic];
}
- 注意: CLPlacemark地標(biāo)對(duì)象沒法直接手動(dòng)創(chuàng)建, 只能通過(反)地理編碼獲取
3. 補(bǔ)充
- 3D視圖
補(bǔ)充1:類似于地圖街景,增強(qiáng)用戶體驗(yàn)
CLLocationCoordinate2D center = CLLocationCoordinate2DMake(23.132931, 113.375924);
MKMapCamera *camera = [MKMapCamera cameraLookingAtCenterCoordinate:center fromEyeCoordinate:CLLocationCoordinate2DMake(center.latitude, center.longitude + 0.001) eyeAltitude:1];
self.mapView.camera = camera;
2.地圖截圖
// 截圖附加選項(xiàng)
MKMapSnapshotOptions *options = [[MKMapSnapshotOptions alloc] init];
// 設(shè)置截圖區(qū)域(在地圖上的區(qū)域,作用在地圖)
options.region = self.mapView.region;
// options.mapRect = self.mapView.visibleMapRect;
// 設(shè)置截圖后的圖片大小(作用在輸出圖像)
options.size = self.mapView.frame.size;
// 設(shè)置截圖后的圖片比例(默認(rèn)是屏幕比例占贫, 作用在輸出圖像)
options.scale = [[UIScreen mainScreen] scale];
MKMapSnapshotter *snapshotter = [[MKMapSnapshotter alloc] initWithOptions:options];
[snapshotter startWithCompletionHandler:^(MKMapSnapshot * _Nullable snapshot, NSError * _Nullable error) {
if (error) {
NSLog(@"截圖錯(cuò)誤:%@",error.localizedDescription);
}else
{
// 設(shè)置屏幕上圖片顯示
self.snapshootImageView.image = snapshot.image;
// 將圖片保存到指定路徑(此處是桌面路徑桃熄,需要根據(jù)個(gè)人電腦不同進(jìn)行修改)
NSData *data = UIImagePNGRepresentation(snapshot.image);
[data writeToFile:@"/Users/wangshunzi/Desktop/snap.png" atomically:YES];
}
}];
4. 測(cè)試環(huán)境
- 加載地圖數(shù)據(jù)需要聯(lián)網(wǎng)
- XCode版本不限
- iOS系統(tǒng)版本不限
5. 常見問題總結(jié)
- 需要注意地標(biāo)對(duì)象不能手動(dòng)創(chuàng)建, 因?yàn)槔锩娴膶傩允莚eadonly; 只能通過(反)地理編碼獲取
六. 獲取導(dǎo)航路線信息
1. 實(shí)現(xiàn)須知
- 獲取導(dǎo)航路線, 需要想蘋果服務(wù)器發(fā)送網(wǎng)絡(luò)請(qǐng)求
- 記住關(guān)鍵對(duì)象MKDirections
2.代碼實(shí)現(xiàn)
// 根據(jù)兩個(gè)地標(biāo),向蘋果服務(wù)器請(qǐng)求對(duì)應(yīng)的行走路線信息
- (void)directionsWithBeginPlackmark:(CLPlacemark *)beginP andEndPlacemark:(CLPlacemark *)endP
{
// 創(chuàng)建請(qǐng)求
MKDirectionsRequest *request = [[MKDirectionsRequest alloc] init];
// 設(shè)置開始地標(biāo)
MKPlacemark *beginMP = [[MKPlacemark alloc] initWithPlacemark:beginP];
request.source = [[MKMapItem alloc] initWithPlacemark:beginMP];
// 設(shè)置結(jié)束地標(biāo)
MKPlacemark *endMP = [[MKPlacemark alloc] initWithPlacemark:endP];
request.destination = [[MKMapItem alloc] initWithPlacemark:endMP];
// 根據(jù)請(qǐng)求,獲取實(shí)際路線信息
MKDirections *directions = [[MKDirections alloc] initWithRequest:request];
[directions calculateDirectionsWithCompletionHandler:^(MKDirectionsResponse * _Nullable response, NSError * _Nullable error) {
[response.routes enumerateObjectsUsingBlock:^(MKRoute * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
NSLog(@"%@--", obj.name);
[obj.steps enumerateObjectsUsingBlock:^(MKRouteStep * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
NSLog(@"%@", obj.instructions);
}];
}];
}];
}
3. 導(dǎo)航路線對(duì)象詳解
/**
MKDirectionsResponse對(duì)象解析
source :開始位置
destination :結(jié)束位置
routes : 路線信息 (MKRoute對(duì)象)
MKRoute對(duì)象解析
name : 路的名稱
advisoryNotices : 注意警告信息
distance : 路線長度(實(shí)際物理距離瞳收,單位是m)
polyline : 路線對(duì)應(yīng)的在地圖上的幾何線路(由很多點(diǎn)組成碉京,可繪制在地圖上)
steps : 多個(gè)行走步驟組成的數(shù)組(例如“前方路口左轉(zhuǎn)”,“保持直行”等等螟深, MKRouteStep 對(duì)象)
MKRouteStep對(duì)象解析
instructions : 步驟說明(例如“前方路口左轉(zhuǎn)”谐宙,“保持直行”等等)
transportType : 通過方式(駕車,步行等)
polyline : 路線對(duì)應(yīng)的在地圖上的幾何線路(由很多點(diǎn)組成界弧,可繪制在地圖上)
注意:
MKRoute是一整條長路凡蜻;MKRouteStep是這條長路中的每一截;
*/
七. 繪制導(dǎo)航路線
1. 理論支持
- 路線也是一個(gè)覆蓋層
- 在地圖上操作覆蓋層,其實(shí)操作的是覆蓋層的數(shù)據(jù)模型
添加覆蓋層:在地圖上添加覆蓋層數(shù)據(jù)模型
刪除覆蓋層:在地圖上移除覆蓋層數(shù)據(jù)模型
2. 添加導(dǎo)航路線到地圖
- 獲取幾何路線的數(shù)據(jù)模型 (id <MKOverlay>)overlay
- 地圖添加覆蓋層(幾何路線也是一個(gè)覆蓋層), 直接添加覆蓋層數(shù)據(jù)模型
[self.mapView addOverlay:overlay];
- 設(shè)置地圖代理, 代理遵循協(xié)議 MKMapViewDelegate
- 實(shí)現(xiàn)地圖添加覆蓋層數(shù)據(jù)模型時(shí), 回調(diào)的代理方法; 通過此方法, 返回對(duì)應(yīng)的渲染圖層
- (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id<MKOverlay>)overlay
{
// 創(chuàng)建折線渲染對(duì)象
if ([overlay isKindOfClass:[MKPolyline class]])
{
MKPolylineRenderer *lineRenderer = [[MKPolylineRenderer alloc] initWithOverlay:overlay];
// 設(shè)置線寬
lineRenderer.lineWidth = 6;
// 設(shè)置線顏色
lineRenderer.strokeColor = [UIColor redColor];
return lineRenderer;
}
}