一. 地圖的基本使用
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之后, 需要主動請求用戶授權
5. 測試環境
- 加載地圖數據需要聯網
- XCode版本根據測試選擇不同版本(iOS9.0 只能使用 XCode7.0版本)
- iOS系統版本根據測試選擇不同版本(例如地圖類型, 在iOS9.0之后才有新增)
6. 常見問題總結
- 地圖加載不顯示?
檢查網絡是否通暢 - 地圖放的太大都是格子, 禁止瀏覽
正常, 為了安全等原因, 不會看的太詳細 - 地圖運行起來APP占用內存非常大
正常, 地圖加載了很多資源 - 用戶位置不顯示
首先, 檢查代碼, 是否有設置顯示用戶位置,是否有進行請求位置授權
其次, 查看模擬器是否有位置信息
第三, 重置模擬器, 模擬器又發神經了.
二. 地圖的中級使用
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. 測試環境
- 加載地圖數據需要聯網
- XCode版本不限
- iOS系統版本不限
6. 常見問題總結
地圖上的藍點為啥不顯示?
第一: 確定代碼是否有誤(例如, 是否顯示了用戶位置)
第二: 確定模擬器是否設置位置
第三: 看下位置在哪, 是不是不在當前地圖顯示區域地圖跨度設置之后, 最終顯示的跨度和設置數值不一致?
因為地球的不是正方形的, 隨著用戶的位置移動, 會自動修正地圖跨度, 保持地圖不變形;
三. 地圖高級-大頭針基本使用
1. 理論支撐(必須掌握)
按照MVC的原則
在地圖上操作大頭針,實際上是控制大頭針數據模型
- 添加大頭針就是添加大頭針數據模型
- 刪除大頭針就是刪除大頭針數據模型
2. 在地圖上添加大頭針視圖
- 自定義大頭針數據模型
- 創建繼承自NSObject的數據模型XMGAnnotation, 遵循大頭針數據模型必須遵循的協議(MKAnnotation)
- 注意將協議@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. 測試環境
- 加載地圖數據需要聯網
- XCode版本不限
- iOS系統版本不限
四. 地圖高級-大頭針的自定義
1. 理論支撐
按照MVC的原則
- 每當添加一個大頭針數據模型時, 地圖就會調用對應的代理方法, 查找對應的大頭針視圖,顯示在地圖上;
- 如果該方法沒有實現, 或者返回nil, 那么就會使用系統默認的大頭針視圖
2. 模擬系統默認的大頭針實現方案
- 實現當添加大頭針數據模型時,地圖回調的代理方法
-(MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(XMGAnnotation *)annotation
{
}
實現須知
大頭針系統對應的視圖是 MKPinAnnotationView,它繼承自 MKAnnotationView
地圖上的大頭針視圖,和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
{
}
實現須知
如果想要自定義大頭針, 必須使用 MKAnnotationView 或者 自定義的子類
但是不能直接使用系統默認的大頭針, 會無效
實現代碼
// 自定義大頭針
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. 測試環境
- 加載地圖數據需要聯網
- XCode版本不限
- iOS系統版本不限
6. 常見問題總結
- 代碼運行在低版本的XCode上, 編譯失敗
第一: 語法錯誤; XCode7.0 對于OC語法優化了一些, 需要手動調整
第二: iOS9.0的SDK, 在XCode7.0之前的版本沒有對應的API
五. 利用系統App導航
1. 導航的三種實現方案
- 可以將需要導航的位置丟給系統的地圖APP進行導航
- 發送網絡請求到公司服務器獲取導航數據, 然后自己手動繪制導航
- 利用三方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;
2.地圖截圖
// 截圖附加選項
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. 測試環境
- 加載地圖數據需要聯網
- XCode版本不限
- iOS系統版本不限
5. 常見問題總結
- 需要注意地標對象不能手動創建, 因為里面的屬性是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是這條長路中的每一截;
*/
七. 繪制導航路線
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;
}
}