iOS從6.0開始地圖服務不再由谷歌驅動,而是改用自家地圖,當然在國內它的數據是由高德地圖提供的。這樣一來,如果在iOS6.0之前進行地圖開發的話使用方法會有所不同,基於目前的情況其實使用iOS6.0之前版本的系統基本已經寥寥無幾了,所有在接下來的內容中不會再針對iOS5及之前版本的地圖開發進行介紹。
在iOS中進行地圖開發主要有兩種方式,一種是直接利用MapKit框架進行地圖開發,利用這種方式可以對地圖進行精准的控制;另一種方式是直接調用蘋果官方自帶的地圖應用,主要用於一些簡單的地圖應用(例如:進行導航覆蓋物填充等),無法進行精確的控制。當然,本節重點內容還是前者,系統地圖的使用會在後面講到。
用MapKit之前需要簡單了解一下,MapKit中的類都是以MK開頭的,其中用於地圖展示的UI控件是MKMapView,下面是MKMapView一些常用屬性和方法,具體如下表:
我們先來看一下如何在地圖上實現用戶位置跟蹤,在很多帶有地圖的應用中默認打開地圖都會顯示用戶當前位置,同時將當前位置標記出來放到屏幕中點方便用戶對周圍情況進行查看。如果在iOS6或者iOS7中實現這個功能只需要添加地圖控件、設置用戶跟蹤模式、在-(void)mapView:(MKMapView )mapView didUpdateUserLocation:(MKUserLocation )userLocation代理方法中設置地圖中心區域及顯示范圍。但是在iOS8中用法稍有不同:
由於在地圖中進行用戶位置跟蹤需要使用定位功能,而定位功能在iOS8中設計發生了變化,因此必須按照前面定位章節中提到的內容進行配置和請求。 iOS8中不需要進行中心點的指定,默認會將當前位置設置中心點並自動設置顯示區域范圍。了解以上兩點,要進行用戶位置跟蹤其實就相當簡單了,值得一提的是-(void)mapView:(MKMapView )mapView didUpdateUserLocation:(MKUserLocation )userLocation這個代理方法。這個方法只有在定位(利用前面章節中的定位內容)到當前位置之後就會調用,以後每當用戶位置發生改變就會觸發,調用頻率相當頻繁。下面是實現用戶位置追蹤的代碼:
示例代碼
#import "ViewController.h"
#import
#import
@interface ViewController ()
@property (nonatomic, strong)CLLocationManager *locationManager;
@property (nonatomic, strong)MKMapView *mapView;
@end
@implementation ViewController
// 定位管家
- (CLLocationManager *)locationManager {
if (!_locationManager) {
_locationManager = [[CLLocationManager alloc] init];
_locationManager.delegate = self;
}
return _locationManager;
}
// 地圖視圖
- (MKMapView *)mapView {
if (!_mapView) {
_mapView = [[MKMapView alloc] initWithFrame:self.view.bounds];
_mapView.delegate = self;
}
return _mapView;
}
- (void)viewDidLoad {
[super viewDidLoad];
// 1.判斷用戶設備定位功能是否開啟
if (![CLLocationManager locationServicesEnabled]) {
NSLog(@"提醒用戶定位服務未開啟");
}
// 2.判斷應用的授權狀態,請求用戶定位服務授權
if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusNotDetermined) {
// iOS8以上的版本才需要獲取定位授權
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0) {
//調用此方法之前必須在plist文件中添加NSLocationWhenInUseUsageDescription --string-- 後面跟的文字就是提示信息
[self.locationManager requestWhenInUseAuthorization];
}
}
// 3.添加地圖到界面
[self.view addSubview:self.mapView];
[self.view sendSubviewToBack:self.mapView];
// 4.設置地圖的常用屬性
self.mapView.mapType = MKMapTypeStandard;// 設置地圖類型
self.mapView.userTrackingMode = MKUserTrackingModeFollow;// 設置用戶追蹤模式,不設置設置追蹤模式地圖不會隨用戶移動
// 地圖的操作屬性
// 設置對應的屬性時,注意該屬性是從哪個系統版本開始引入的,做好不同系統版本的適配
self.mapView.zoomEnabled = YES;// 是否可以縮放
self.mapView.scrollEnabled = YES;// 是否可以滾動
self.mapView.rotateEnabled = YES;// 是否可以旋轉
self.mapView.pitchEnabled = YES;// 是否顯示3D
// 地圖的顯示屬性
self.mapView.showsCompass = YES;// 是否顯示指南針
self.mapView.showsScale = YES;// shif顯示比例尺
self.mapView.showsTraffic = YES;// 是否顯示交通狀況
self.mapView.showsBuildings = YES;// 是否顯示建築物
self.mapView.showsUserLocation = YES;// 是否顯示用戶的位置:會在地圖顯示一個藍色的圓點,標記當前位置,
}
#pragma mark - MKMapViewDelegate
// 加載地圖完成後調用,每次地圖顯示區域發生改變時都會加載地圖並調用此方法
- (void)mapViewDidFinishLoadingMap:(MKMapView *)mapView {
NSLog(@"地圖加載完成");
}
/**
* 方法說明:當用戶的位置更新,就會調用(不斷地監控用戶的位置,調用頻率特別高)
*
* @param userLocation 表示地圖上藍色那顆大頭針的數據(注意不是那個藍色的視圖,不復寫該方法也可以顯示藍色的大頭針)
*/
- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation {
//用戶位置的坐標
CLLocationCoordinate2D center = userLocation.location.coordinate;
NSLog(@"%f %f", center.latitude, center.longitude);
// 可以定義用戶位置的大頭針屬性
userLocation.title = @"北京";
userLocation.subtitle = @"天朝帝都";
// 刷新用戶位置時,地圖顯示的區域默認以用戶位置為中心,但是默認顯示的區域范圍較小,我們可以在這裡設置一下地圖顯示的區域
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
// 實踐發現,在定位刷新結束後立即調用該方法是無法修改地圖顯示區域的范圍的,所以我們延遲調用一下,哪怕僅僅是0.1秒
[self setMapViewRegin];
});
}
// 屏幕中顯示的地圖區域(中心點或是范圍)發生變化時調用該方法
- (void)mapView:(MKMapView *)mapView regionWillChangeAnimated:(BOOL)animated {
//附近的人,附近的商家,顯示的地圖區域改變時,顯示的數據也需要改變
NSLog(@"顯示的地圖范圍將要發生變化");
}
- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated {
NSLog(@"顯示的地圖范圍已經發生變化");
// 地圖顯示區域
MKCoordinateRegion region = mapView.region;
CLLocationCoordinate2D center = region.center;
NSLog(@"中心點緯度:%f 經度:%f",center.latitude,center.longitude);
MKCoordinateSpan span = region.span;
NSLog(@"顯示區域范圍%f %f",span.latitudeDelta,span.longitudeDelta);
}
#pragma mark - UIActions
- (IBAction)backUserLocation:(id)sender {
[self showUserLocation];
// [self setMapViewRegin];
}
#pragma mark - 私有方法
// 顯示用戶所在位置
- (void)showUserLocation {
// 注意該方法應該在確定定位成功之後設置,否則無法獲取用戶的位置
// 獲取用戶位置的坐標
CLLocationCoordinate2D coordinate = self.mapView.userLocation.location.coordinate;
[self.mapView setCenterCoordinate:coordinate animated:YES];
}
// 設置地圖的顯示區域
- (void)setMapViewRegin {
// 注意該方法應該在確定定位成功之後設置,否則無法獲取用戶的位置
// 獲取用戶位置作為地圖顯示區域中心
CLLocationCoordinate2D coordinate = self.mapView.userLocation.location.coordinate;
// 定義經緯度作為地圖顯示區域范圍
MKCoordinateSpan span = MKCoordinateSpanMake(1, 1);
// 定義地圖顯示的區域
MKCoordinateRegion regin = MKCoordinateRegionMake(coordinate, span);
// 設置地圖顯示區域
[self.mapView setRegion:regin animated:YES];
}
@end
注意:上面的代碼有一個坑點,就是當我們刷新用戶位置時,首次看到的地圖顯示區域是默認的,但是默認顯示區域的范圍是很小的,我們要想看到跟大的區域只能在代理方法中自行設置,但是我們看到,如果在位置刷新後立即設置的話,是沒有任何的效果的。我們需要延遲調用設置顯示區域的方法,哪怕僅僅是0.1秒。另外,模擬器默認的坐標為蘋果總部,我們可以自定義模擬器所在的地理位置。
另外,在上面的代碼中,有幾個結構體需要在這裡額外的做一些說明:
MKCoordinateRegion 用來表示地圖顯示區域的一個結構體
typedef struct {
CLLocationCoZ喎?/kf/ware/vc/" target="_blank" class="keylink">vcmRpbmF0ZTJEIGNlbnRlcjsvL8f40/K1xNbQ0MS14zxiciAvPg0KTUtDb29yZGluYXRlU3BhbiBzcGFuOy8vx/jT8r/ntsg8YnIgLz4NCn0gTUtDb29yZGluYXRlUmVnaW9uOzwvcD4NCjxwPkNMTG9jYXRpb25Db29yZGluYXRlMkQg08PAtLHtyr612MDt1/ix6rXE0ru49r3hubnM5TxiciAvPg0KdHlwZWRlZiBzdHJ1Y3QgezxiciAvPg0KQ0xMb2NhdGlvbkRlZ3JlZXMgbGF0aXR1ZGU7Ly8g1/ix6s6ztsg8YnIgLz4NCkNMTG9jYXRpb25EZWdyZWVzIGxvbmdpdHVkZTsvLyDX+LHqvq22yDxiciAvPg0KfSBDTExvY2F0aW9uQ29vcmRpbmF0ZTJEOzwvcD4NCjxwPk1LQ29vcmRpbmF0ZVNwYW4g08PAtLHtyr612M28z9TKvsf40/K/57bItcTSu7j2veG5uczlPGJyIC8+DQp0eXBlZGVmIHN0cnVjdCB7PGJyIC8+DQpDTExvY2F0aW9uRGVncmVlcyBsYXRpdHVkZURlbHRhOy8vzrO2yL/ntsg8YnIgLz4NCkNMTG9jYXRpb25EZWdyZWVzIGxvbmdpdHVkZURlbHRhOy8vvq22yL/ntsg8YnIgLz4NCn0gTUtDb29yZGluYXRlU3Bhbjs8L3A+DQo8aHIgLz4NCjxoMiBpZD0="2-添加地圖標記">2. 添加地圖標記
下面我們來看一下iOS中如何在地圖中標記位置,運行上面的代碼,我們可以在地圖上看到一個藍色的圓點,用來標記用戶的位置。在iOS地圖開發中經常會需要標記某個位置,需要使用地圖標注,也就是大家俗稱的“大頭針”。
只要一個NSObject類實現MKAnnotation協議就可以作為一個大頭針。我們在上面的代碼中獲取的用戶位置數據都是封裝在一個MKUserLocation實例對象中的,MKUserLocation就是一個實現了MKAnnotation協議的NSObject子類。但是MKUserLocation的屬性大多都是只讀的,所以我們不能直接使用MKUserLocation類來保存大頭針數據。我們可以自己實現一個NSObject子類並實現MKAnnotation協議作為我們添加的大頭針的數據模型。一般我們在自定的子類中重寫協議中coordinate(標記位置)、title(標題)、subtitle(子標題)三個屬性,然後在程序中創建大頭針對象並調用addAnnotation:方法添加大頭針即可;
示例代碼:
自定義大頭針數據模型:
Annotation.h 大頭針數據模型
#import
#import
@interface Annotation : NSObject
// 大頭針標記的地理坐標
@property (nonatomic, assign) CLLocationCoordinate2D coordinate;
// 大頭針標題
@property (nonatomic, copy) NSString *title;
// 大頭針子標題
@property (nonatomic, copy) NSString *subtitle;
@end
根據大頭針數據模型,填充地圖標記數據,並添加大頭針
#import "ViewController.h" #import <corelocation corelocation.h=""> #import <mapkit mapkit.h=""> #import "Annotation.h"// 自定義大頭針數據模型 @interface ViewController ()<cllocationmanagerdelegate,mkmapviewdelegate> @property (nonatomic, strong)CLLocationManager *locationManager; @property (nonatomic, strong)MKMapView *mapView; @end @implementation ViewController - (CLLocationManager *)locationManager { if (!_locationManager) { _locationManager = [[CLLocationManager alloc] init]; _locationManager.delegate = self; } return _locationManager; } - (MKMapView *)mapView { if (!_mapView) { _mapView = [[MKMapView alloc] initWithFrame:self.view.bounds]; //設置mapView的屬性 self.mapView.mapType = MKMapTypeStandard;//默認 //設置用戶跟蹤模式 self.mapView.userTrackingMode = MKUserTrackingModeFollow; _mapView.delegate = self; } return _mapView; } - (void)viewDidLoad { [super viewDidLoad]; // 添加地圖 [self.view addSubview:self.mapView]; //開啟定位服務 //1.判斷手機定位服務是否打開 if (![CLLocationManager locationServicesEnabled]) { NSLog(@"手機定位服務沒有打開"); return; } //2.iOS8.0以上的用戶需要授權 if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusNotDetermined) { if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0) { //調用此方法之前必須在plist文件中添加NSLocationWhenInUseUsageDescription --string-- 後面跟的文字就是提示信息 [self.locationManager requestWhenInUseAuthorization]; } } //添加單擊的手勢,添加大頭針 [self.mapView addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapAction:)]]; } //將大頭針添加到鼠標(手指)點擊的地圖位置 - (void)tapAction:(UITapGestureRecognizer *)tap { //1.獲取用戶手指觸摸屏幕的坐標(x,y) CGPoint point = [tap locationInView:tap.view]; NSLog(@"%@",NSStringFromCGPoint(point)); //2.將屏幕上的坐標轉換成經緯度(數學坐標轉換成地理經緯度) //- (CLLocationCoordinate2D)convertPoint:(CGPoint)point toCoordinateFromView:(UIView *)view; CLLocationCoordinate2D location = [self.mapView convertPoint:point toCoordinateFromView:self.mapView]; NSLog(@"(%f,%f)",location.latitude,location.longitude); //MKUserLocation屬性為只讀的,不能作為自定義大頭針的類存在 // MKUserLocation *anno = [[MKUserLocation alloc] init]; // anno.location = [[CLLocation alloc] initWithLatitude:coordinate.latitude longitude:coordinate.longitude]; //3.子類化大頭針模型,實現大頭針協議<mkannotation> //4.創建大頭針 Annotation *anno = [[Annotation alloc] init]; anno.coordinate = location;//設置坐標 anno.title = @"貝沃匯力"; anno.subtitle = @"iOS 培訓的黃埔軍校"; //4.添加 [self.mapView addAnnotation:anno]; // self.mapView addAnnotation:<#(nonnull id<mkannotation>)#> } #pragma mapViewDelegate /** * 用戶位置發生改變的時候調用這個方法 * * @param mapView 地圖 * @param userLocation 地圖上藍色的那顆大頭針數據(注意不是那個藍色的視圖)不復寫該方法也可以顯示 */ - (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation { userLocation.title = @"北京市"; userLocation.subtitle = @"天朝帝都"; NSLog(@"%f , %f",userLocation.location.coordinate.latitude,userLocation.location.coordinate.longitude); NSLog(@"%@",userLocation); //設置地圖顯示的區域 //獲取中心點位置 CLLocationCoordinate2D center = userLocation.location.coordinate; //顯示跨度 MKCoordinateSpan span = MKCoordinateSpanMake(3, 3*(375/667)); //region MKCoordinateRegion region = MKCoordinateRegionMake(center, span); [mapView setRegion:region animated:YES]; } @end