MapKit框架的使用
一. 地圖的基本使用
1. 設置地圖顯示類型
地圖的樣式可以手動設置, 在iOS9.0之前有3種, iOS9.0之后增加了2種
-
設置方式
self.mapView.mapType = MKMapTypeStandard;
枚舉類型 對應含義 MKMapTypeStandard 標準地圖 MKMapTypeSatellite 衛星地圖 MKMapTypeHybrid 混合模式(標準+衛星) MKMapTypeSatelliteFlyover 3D立體衛星(iOS9.0) MKMapTypeHybridFlyover 3D立體混合(iOS9.0)
2. 設置地圖控制項
地圖的旋轉, 縮放, 移動等等操作行為都可以開啟或者關閉
-
設置方式
self.customMapView.zoomEnabled = YES; // 是否縮放 self.customMapView.scrollEnabled = YES; // 是否滾動 self.customMapView.rotateEnabled = YES; // 是否旋轉 self.customMapView.pitchEnabled = NO; // 是否顯示3DVIEW
3. 設置地圖顯示項
地圖上的指南針, 比例尺, 建筑物, POI點都可以控制是否顯示
設置方式
self.customMapView.showsCompass = YES; // 是否顯示指南針
self.customMapView.showsScale = YES; // 是否顯示比例尺
self.customMapView.showsTraffic = YES; // 是否顯示交通
self.customMapView.showsBuildings = YES; // 是否顯示建筑物
4. 顯示用戶位置
可以設置顯示用戶當前所在位置, 以一個藍點的形式呈現在地圖上
-
設置方式
方案1: self.customMapView.showsUserLocation = YES; 效果: 會在地圖上顯示一個藍點, 標識用戶所在位置; 但地圖不會縮放, 而且當用戶位置移動時, 地圖不會跟隨用戶位置移動而移動 方案2: self.customMapView.userTrackingMode = MKUserTrackingModeFollowWithHeading; 效果: 會在地圖上顯示一個藍點, 標識用戶所在位置; 而且地圖縮放到合適比例,顯示用戶位置, 當用戶位置移動時, 地圖會跟隨用戶位置移動而移動; 但是有時候失效;
注意事項: 如果要顯示用戶位置, 在iOS8.0之后, 需要主動請求用戶授權
4. 測試環境
1. 加載地圖數據需要聯網
2. XCode版本根據測試選擇不同版本(iOS9.0 只能使用 XCode7.0版本)
3. iOS系統版本根據測試選擇不同版本(例如地圖類型, 在iOS9.0之后才有新增)
5. 常見問題總結
1. 地圖加載不顯示?
檢查網絡是否通暢
2. 地圖放的太大都是格子, 禁止瀏覽
正常, 為了安全等原因, 不會看的太詳細
3. 地圖運行起來APP占用內存非常大
正常, 地圖加載了很多資源
4. 用戶位置不顯示
首先, 檢查代碼, 是否有設置顯示用戶位置,是否有進行請求位置授權
其次, 查看模擬器是否有位置信息
第三, 重置模擬器, 模擬器又發神經了.
二. 地圖的中級使用
1. 查看當前用戶位置信息
設置地圖代理
-
實現代理方法
-(void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation { NSLog(@"%@", userLocation); }
2. 調整地圖顯示中心
-
確定地圖中心經緯度坐標
CLLocationCoordinate2D center = CLLocationCoordinate2DMake(21.123, 121.345);
-
設置地圖中心為給定的經緯度坐標
[mapView setCenterCoordinate:center animated:YES];
3. 調整地圖顯示區域
-
獲取合適的區域跨度
實現當地圖區域發生改變時調用的代理代理方法, 并調整地圖區域到合適比例, 并在對應的方法中, 獲取對應的跨度信息 代碼如下: - (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated { NSLog(@"%f---%f", mapView.region.span.latitudeDelta, mapView.region.span.longitudeDelta); }
-
創建一個區域(包含區域中心, 和區域跨度)
CLLocationCoordinate2D center = CLLocationCoordinate2DMake(21.123, 121.345); MKCoordinateSpan span = MKCoordinateSpanMake(0.1, 0.1); MKCoordinateRegion region = MKCoordinateRegionMake(center, span);
-
設置地圖顯示區域
[self.mapView setRegion:region animated:YES];
-
跨度概念解釋
MKCoordinateSpan 跨度解釋: latitudeDelta:緯度跨度,因為南北緯各90.0度,所以此值的范圍是(0.0---180.0);此值表示,整個地圖視圖寬度,顯示多大跨度; longitudeDelta:經度跨度,因為東西經各180.0度,所以此值范圍是(0.0---360.0):此值表示,整個地圖視圖高度,顯示多大跨度; 注意:地圖視圖顯示,不會更改地圖的比例,會以地圖視圖高度或寬度較小的那個為基準,按比例調整
4. MKUserLocation 大頭針數據模型詳解
MKUserLocation : 被稱作“大頭針(數據)模型”;
其實喊什么都行,本質就是一個數據模型,只不過此模型遵循了大頭針要遵循的協議(MKAnnotation)
重要屬性:
location : 用戶當前所在位置信息(CLLocation對象)
title : 大頭針標注要顯示的標題(NSString對象)
subtitle : 大頭針標注要顯示的子標題(NSString對象)
5. 測試環境
1. 加載地圖數據需要聯網
2. XCode版本不限
3. iOS系統版本不限
6. 常見問題總結
1. 地圖上的藍點為啥不顯示?
第一: 確定代碼是否有誤(例如, 是否顯示了用戶位置)
第二: 確定模擬器是否設置位置
第三: 看下位置在哪, 是不是不在當前地圖顯示區域
2. 地圖跨度設置之后, 最終顯示的跨度和設置數值不一致?
因為地球的不是正方形的, 隨著用戶的位置移動, 會自動修正地圖跨度, 保持地圖不變形;
三. 地圖高級-大頭針基本使用
1. 理論支撐(必須掌握)
按照MVC的原則
* 在地圖上操作大頭針,實際上是控制大頭針數據模型
1. 添加大頭針就是添加大頭針數據模型
2. 刪除大頭針就是刪除大頭針數據模型
2. 在地圖上添加大頭針視圖
-
自定義大頭針數據模型
1) 創建繼承自NSObject的數據模型XMGAnnotation, 遵循大頭針數據模型必須遵循的協議(MKAnnotation) 2) 注意將協議@property 中的readonly 去掉;
-
創建大頭針數據模型, 并初始化參數
XMGAnnotation *annotation = [[XMGAnnotation alloc] init]; annotation.coordinate = coordinate; annotation.title = @"小碼哥"; annotation.subtitle = @"小碼哥分部";
-
調用地圖的添加大頭針數據模型方法
[self.customMapView addAnnotation:annotation];
3. 移除大頭針(所有大頭針)
NSArray *annotations = self.customMapView.annotations;
[self.customMapView removeAnnotations:annotations];
4. 場景模擬
-
場景描述:
鼠標點擊在地圖哪個位置, 就在對應的位置添加一個大頭針, 并在標注彈框中顯示對應的城市和街道;
-
實現步驟
1. 獲取觸摸點在地圖上對應的坐標 UITouch *touch = [touches anyObject]; CGPoint touchPoint = [touch locationInView:self.customMapView]; 2. 將坐標轉換成為經緯度 CLLocationCoordinate2D center = [self.customMapView convertPoint:touchPoint toCoordinateFromView:self.customMapView]; 3. 根據經緯度創建大頭針數據模型, 并添加在地圖上 XMGAnnotation *annotation = [[XMGAnnotation alloc] init]; annotation.coordinate = coordinate; annotation.title = @"小碼哥"; annotation.subtitle = @"小碼哥分部"; [self.customMapView addAnnotation:annotation]; 4. 利用反地理編碼, 獲取該點對應的城市和街道名稱, 然后修改大頭針數據模型 注意: 設置彈框數據時, 對應的大頭針數據模型應有對應的占位數據(這樣對應的UI才會生成,后面才能重新修改數據)
5. 測試環境
1. 加載地圖數據需要聯網
2. XCode版本不限
3. iOS系統版本不限
6. 常見問題總結
1. 反地理編碼無法獲取對應的數據
第一: 檢查是否有聯網
第二: 檢查代碼是否有誤
第三: 有時存在某些位置沒有反地理編碼結果, 換個點嘗試, 如果都沒有, 排除此原因
四. 地圖高級-大頭針的自定義
1. 理論支撐
按照MVC的原則
1. 每當添加一個大頭針數據模型時, 地圖就會調用對應的代理方法, 查找對應的大頭針視圖,顯示在地圖上;
2. 如果該方法沒有實現, 或者返回nil, 那么就會使用系統默認的大頭針視圖
2. 模擬系統默認的大頭針實現方案
-
實現當添加大頭針數據模型時,地圖回調的代理方法
-(MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(XMGAnnotation *)annotation { }
-
實現須知
1. 大頭針系統對應的視圖是 MKPinAnnotationView,它繼承自 MKAnnotationView 2. 地圖上的大頭針視圖,和tableview上的cell一樣,都使用“循環利用”的機制
-
實現代碼
static NSString *pinID = @"pinID"; MKPinAnnotationView *pinView = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:pinID]; if (!pinView) { pinView = [[MKPinAnnotationView alloc] initWithAnnotation:nil reuseIdentifier:pinID]; } pinView.annotation = annotation; // 彈出標注 pinView.canShowCallout = YES; // 修改大頭針顏色 pinView.pinColor = MKPinAnnotationColorPurple; // 設置大頭針從天而降 pinView.animatesDrop = YES; // 設置大頭針可以被拖拽(父類中的屬性) pinView.draggable = YES; return pinView;
3. 自定義大頭針
-
實現當添加大頭針數據模型時,地圖回調的代理方法
-(MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(XMGAnnotation *)annotation { }
-
實現須知
1. 如果想要自定義大頭針, 必須使用 MKAnnotationView 或者 自定義的子類 2. 但是不能直接使用系統默認的大頭針, 會無效
-
實現代碼
// 自定義大頭針 static NSString *pinID = @"pinID"; MKAnnotationView *customPinView = [mapView dequeueReusableAnnotationViewWithIdentifier:pinID]; if (!customPinView) { customPinView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:pinID]; } // 設置大頭針圖片 customPinView.image = [UIImage imageNamed:@"category_3"]; // 設置大頭針可以彈出標注 customPinView.canShowCallout = YES; // 設置標注左側視圖 UIImageView *leftIV = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 40, 40)]; leftIV.image = [UIImage imageNamed:@"huba.jpeg"]; customPinView.leftCalloutAccessoryView = leftIV; // 設置標注右側視圖 UIImageView *rightIV = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 40, 40)]; rightIV.image = [UIImage imageNamed:@"eason.jpg"]; customPinView.rightCalloutAccessoryView = rightIV; // 設置標注詳情視圖(iOS9.0) customPinView.detailCalloutAccessoryView = [[UISwitch alloc] init]; return customPinView;
4. 代理方法補充
-
選中一個大頭針時調用
-(void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view { NSLog(@"選中%@", [view.annotation title]); }
-
取消選中大頭針時調用
-(void)mapView:(MKMapView *)mapView didDeselectAnnotationView:(MKAnnotationView *)view { NSLog(@"取消選中%@", [view.annotation title]); }
5. 測試環境
1. 加載地圖數據需要聯網
2. XCode版本不限
3. iOS系統版本不限
6. 常見問題總結
1. 代碼運行在低版本的XCode上, 編譯失敗
第一: 語法錯誤; XCode7.0 對于OC語法優化了一些, 需要手動調整
第二: iOS9.0的SDK, 在XCode7.0之前的版本沒有對應的API
五. 利用系統App導航
1. 導航的三種實現方案
1. 可以將需要導航的位置丟給系統的地圖APP進行導航
2. 發送網絡請求到公司服務器獲取導航數據, 然后自己手動繪制導航
3. 利用三方SDK實現導航(百度)
2. 直接將起點和終點, 傳遞給系統地圖, 利用系統APP, 進行導航
利用"反推法", 記住關鍵代碼即可
-
代碼如下:
// 根據兩個地標對象進行調用系統導航 - (void)beginNavWithBeginPlacemark:(CLPlacemark *)beginPlacemark andEndPlacemark:(CLPlacemark *)endPlacemark { // 創建起點:根據 CLPlacemark 地標對象創建 MKPlacemark 地標對象 MKPlacemark *itemP1 = [[MKPlacemark alloc] initWithPlacemark:beginPlacemark]; MKMapItem *item1 = [[MKMapItem alloc] initWithPlacemark:itemP1]; // 創建終點:根據 CLPlacemark 地標對象創建 MKPlacemark 地標對象 MKPlacemark *itemP2 = [[MKPlacemark alloc] initWithPlacemark:endPlacemark]; MKMapItem *item2 = [[MKMapItem alloc] initWithPlacemark:itemP2]; NSDictionary *launchDic = @{ // 設置導航模式參數 MKLaunchOptionsDirectionsModeKey : MKLaunchOptionsDirectionsModeDriving, // 設置地圖類型 MKLaunchOptionsMapTypeKey : @(MKMapTypeHybridFlyover), // 設置是否顯示交通 MKLaunchOptionsShowsTrafficKey : @(YES), }; // 根據 MKMapItem 數組 和 啟動參數字典 來調用系統地圖進行導航 [MKMapItem openMapsWithItems:@[item1, item2] launchOptions:launchDic]; }
注意: CLPlacemark地標對象沒法直接手動創建, 只能通過(反)地理編碼獲取
3. 補充
-
3D視圖
補充1:類似于地圖街景,增強用戶體驗 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;
-
地圖截圖
// 截圖附加選項 MKMapSnapshotOptions *options = [[MKMapSnapshotOptions alloc] init]; // 設置截圖區域(在地圖上的區域,作用在地圖) options.region = self.mapView.region; // options.mapRect = self.mapView.visibleMapRect; // 設置截圖后的圖片大小(作用在輸出圖像) options.size = self.mapView.frame.size; // 設置截圖后的圖片比例(默認是屏幕比例, 作用在輸出圖像) options.scale = [[UIScreen mainScreen] scale]; MKMapSnapshotter *snapshotter = [[MKMapSnapshotter alloc] initWithOptions:options]; [snapshotter startWithCompletionHandler:^(MKMapSnapshot * _Nullable snapshot, NSError * _Nullable error) { if (error) { NSLog(@"截圖錯誤:%@",error.localizedDescription); }else { // 設置屏幕上圖片顯示 self.snapshootImageView.image = snapshot.image; // 將圖片保存到指定路徑(此處是桌面路徑,需要根據個人電腦不同進行修改) NSData *data = UIImagePNGRepresentation(snapshot.image); [data writeToFile:@"/Users/wangshunzi/Desktop/snap.png" atomically:YES]; } }];
4. 測試環境
1. 加載地圖數據需要聯網
2. XCode版本不限
3. iOS系統版本不限
5. 常見問題總結
1. 需要注意地標對象不能手動創建, 因為里面的屬性是readonly; 只能通過(反)地理編碼獲取
六. 獲取導航路線信息
1. 實現須知
- 獲取導航路線, 需要想蘋果服務器發送網絡請求
- 記住關鍵對象MKDirections
2.代碼實現
// 根據兩個地標,向蘋果服務器請求對應的行走路線信息
- (void)directionsWithBeginPlackmark:(CLPlacemark *)beginP andEndPlacemark:(CLPlacemark *)endP
{
// 創建請求
MKDirectionsRequest *request = [[MKDirectionsRequest alloc] init];
// 設置開始地標
MKPlacemark *beginMP = [[MKPlacemark alloc] initWithPlacemark:beginP];
request.source = [[MKMapItem alloc] initWithPlacemark:beginMP];
// 設置結束地標
MKPlacemark *endMP = [[MKPlacemark alloc] initWithPlacemark:endP];
request.destination = [[MKMapItem alloc] initWithPlacemark:endMP];
// 根據請求,獲取實際路線信息
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. 導航路線對象詳解
/**
MKDirectionsResponse對象解析
source :開始位置
destination :結束位置
routes : 路線信息 (MKRoute對象)
MKRoute對象解析
name : 路的名稱
advisoryNotices : 注意警告信息
distance : 路線長度(實際物理距離,單位是m)
polyline : 路線對應的在地圖上的幾何線路(由很多點組成,可繪制在地圖上)
steps : 多個行走步驟組成的數組(例如“前方路口左轉”,“保持直行”等等, MKRouteStep 對象)
MKRouteStep對象解析
instructions : 步驟說明(例如“前方路口左轉”,“保持直行”等等)
transportType : 通過方式(駕車,步行等)
polyline : 路線對應的在地圖上的幾何線路(由很多點組成,可繪制在地圖上)
注意:
MKRoute是一整條長路;MKRouteStep是這條長路中的每一截;
*/
4. 測試環境
1. 請求路線數據需要聯網
2. XCode版本不限
3. iOS系統版本不限
5. 常見問題總結
1. 類太多, 記不住咋辦?
此功能不常用, 只需要知道有這一個功能. 如果到時用到, 直接回過頭來找代碼;
七. 繪制導航路線
1. 理論支持
- 路線也是一個覆蓋層
- 在地圖上操作覆蓋層,其實操作的是覆蓋層的數據模型
添加覆蓋層:在地圖上添加覆蓋層數據模型
刪除覆蓋層:在地圖上移除覆蓋層數據模型
2. 添加導航路線到地圖
獲取幾何路線的數據模型 (id <MKOverlay>)overlay
-
地圖添加覆蓋層(幾何路線也是一個覆蓋層), 直接添加覆蓋層數據模型
[self.mapView addOverlay:overlay];
設置地圖代理, 代理遵循協議 MKMapViewDelegate
-
實現地圖添加覆蓋層數據模型時, 回調的代理方法; 通過此方法, 返回對應的渲染圖層
- (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id<MKOverlay>)overlay { // 創建折線渲染對象 if ([overlay isKindOfClass:[MKPolyline class]]) { MKPolylineRenderer *lineRenderer = [[MKPolylineRenderer alloc] initWithOverlay:overlay]; // 設置線寬 lineRenderer.lineWidth = 6; // 設置線顏色 lineRenderer.strokeColor = [UIColor redColor]; return lineRenderer; } }
3. 練習: 添加圓形覆蓋層到地圖
-
創建圓形區域覆蓋層的數據模型
MKCircle *circle = [MKCircle circleWithCenterCoordinate:self.mapView.centerCoordinate radius:1000000];
-
添加覆蓋層數據模型
[self.mapView addOverlay:circle];
-
實現代理方法
-(MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id<MKOverlay>)overlay { // 創建圓形區域渲染對象 if ([overlay isKindOfClass:[MKCircle class]]) { MKCircleRenderer *circleRender = [[MKCircleRenderer alloc] initWithOverlay:overlay]; circleRender.fillColor = [UIColor cyanColor]; circleRender.alpha = 0.6; return circleRender; } return nil; }
4. 測試環境
1. 地圖加載需要聯網
2. XCode版本不限
3. iOS系統版本不限
5. 常見問題總結
1. 東西太多, 記不住?
只需要記得一個思想, 按照MVC的原則, 我們操作覆蓋層, 就是操作覆蓋層數據模型; 然后地圖, 會調用其對應的代理方法, 獲取對應的覆蓋層渲染層;
類記不住沒關系, 主要記住大致思路就可以.
八. 集成百度地圖
1. 集成原因
1. 有些功能, 系統自帶的高德地圖無法實現, 例如POI檢索等等
2. 一般實現導航功能, 會集成百度地圖的比較多;
2. 集成步驟
1. 下載對應的SDK
2. 按照集成文檔一步一步實現
3. 開發經驗
1. 不要把所有的功能全部都寫在控制器當中, 最好封裝成一個單獨的工具類
2. 如果集成過程中出現問題, 先查看官方文檔
4. 測試環境
1. 需要聯網
2. XCode版本不限
3. iOS系統版本不限
5. 常見問題總結
按照開發文檔一步一步做, 一般沒有什么問題; 注意集成細節就行.