iOS學習筆記20-地圖(二)MapKit架構
一、地圖開發介紹
從iOS6.0開始地圖資料不再由Google驅動,而是改用自家地圖,當然在國內它的資料是由高德地圖提供的。
在iOS中進行地圖開發主要有三種方式:利用
MapKit
架構進行地圖開發,利用這種方式可以對地圖進行精準的控制調用蘋果官方內建的地圖應用,主要用於一些簡單的地圖應用,無法精確控制使用第三方地圖開發SDK庫
用得最多的還是MapKit
,所以這節就只講MapKit
的使用。
二、MapKit核心類
MapKit
的核心類為地圖展示控制項MKMapView
,以下是常用的屬性、對象方法以及代理方法。
1. 屬性:
/* 使用者位置跟蹤 */@property (nonatomic) BOOL showsUserLocation;/*< 是否在地圖上標註使用者位置 */@property (nonatomic, readonly) MKUserLocation *userLocation;/*< 使用者位置 */@property (nonatomic) MKUserTrackingMode userTrackingMode;/*< 使用者跟蹤類型 */typedef NS_ENUM(NSInteger, MKUserTrackingMode) { MKUserTrackingModeNone = 0, /*< 不跟蹤 */ MKUserTrackingModeFollow, /*< 跟蹤 */ MKUserTrackingModeFollowWithHeading, /*< 導航跟蹤 */};/* 設定地圖配置項 */@property (nonatomic) MKMapType mapType;/*< 地圖類型 */@property (nonatomic, readonly) NSArray *annotations;/*< 圖釘數組 */typedef NS_ENUM(NSUInteger, MKMapType) { MKMapTypeStandard = 0,/*< 標準地圖 */ MKMapTypeSatellite,/*< 衛星地圖 */ MKMapTypeHybrid,/*< 混合模式(標準+衛星) */ MKMapTypeSatelliteFlyover,/*< 3D立體衛星(iOS9.0) */ MKMapTypeHybridFlyover,/*< 3D立體混合(iOS9.0) */}/* 設定地圖控制項 */@property (nonatomic) BOOL zoomEnabled;/*< 是否可以縮放 */@property (nonatomic) BOOL scrollEnabled;/*< 是否可以滾動 */@property (nonatomic) BOOL rotateEnabled;/*< 是否可以旋轉 */@property (nonatomic) BOOL pitchEnabled;/*< 是否顯示3D視角 *//* 設定地圖顯示項目 */@property (nonatomic) BOOL showsBuildings;/*< 是否顯示建築物,隻影響標準地圖 */@property (nonatomic) BOOL showsTraffic;/*< 是否顯示交通,iOS9 */@property (nonatomic) BOOL showsCompass;/*< 是否顯示指南針,iOS9 */@property (nonatomic) BOOL showsScale;/*< 是否顯示比例尺,iOS9 */
所謂圖釘就是地圖上顯示的這個標註:
2. 對象方法:
/* 添加圖釘 */- (void)addAnnotation:(id )annotation;- (void)addAnnotations:(NSArray> *)annotations;/* 刪除圖釘 */- (void)removeAnnotation:(id )annotation;- (void)removeAnnotations:(NSArray> *)annotations;/* 選中圖釘與取消選中圖釘 */- (void)selectAnnotation:(id )annotation animated:(BOOL)animated;- (void)deselectAnnotation:(id )annotation animated:(BOOL)animated;/* 擷取圖釘視圖 */- (MKAnnotationView *)viewForAnnotation:(id )annotation;/* 從緩衝池中取出圖釘視圖控制項 */- (MKAnnotationView *)dequeueReusableAnnotationViewWithIdentifier:(NSString *)identifier;/* 設定顯示地區以及地圖中心座標 */- (void)setRegion:(MKCoordinateRegion)region animated:(BOOL)animated;- (void)setCenterCoordinate:(CLLocationCoordinate2D)coordinate animated:(BOOL)animated;/* 經緯度座標轉UIKit座標,UIKit座標轉經緯度座標 */- (CGPoint)convertCoordinate:(CLLocationCoordinate2D)coordinate toPointToView:(UIView *)view;- (CLLocationCoordinate2D)convertPoint:(CGPoint)point toCoordinateFromView:(UIView *)view;
3. 常用代理方法
MKMapViewDelegate
:
/* 地圖載入完成會調用 */- (void)mapViewDidFinishLoadingMap:(MKMapView *)mapView;/* 地圖載入失敗會調用 */- (void)mapViewDidFailLoadingMap:(MKMapView *)mapView withError:(NSError *)error;/* 使用者位置發生改變會調用 */- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation;/* 顯示地區改變會調用 */- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated;/* 點擊選中圖釘時會調用 */- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view;/* 取消選中圖釘時會調用 */- (void)mapView:(MKMapView *)mapView didDeselectAnnotationView:(MKAnnotationView *)view;/* 顯示地圖上的圖釘,功能類似於UITableView的tableView:cellForRowAtIndexPath:方法 */- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id)annotation;
三、MapKit使用1. 首先添加標頭檔:
#import
2. 初始化地圖展示控制項MKMapView
- (void)initMapView{ CGFloat x = 0; CGFloat y = 20; CGFloat width = self.view.frame.size.width; CGFloat height = self.view.frame.size.height; //建立MKMapView,設定控制項視圖大小 MKMapView *mapView = [[MKMapView alloc] initWithFrame:CGRectMake(x, y, width, height)]; //設定地圖類型 mapView.mapType = MKMapTypeStandard; //設定代理 mapView.delegate = self; [self.view addSubview:mapView]; self.mapView = mapView;}
3. 使用者位置跟蹤在iOS8之前,實現這個功能只需要:設定使用者跟蹤模式在
mapView:DidUpdateUserLocation:
代理方法中設定地圖中心和顯示範圍在iOS8之後,用法稍有不同:必須按照前面的定位章節的,擷取前台或者前背景定位服務授權,下面是連結:
iOS學習筆記19-地圖(一)定位CoreLocation 不需要進行中心點的指定,預設會將當前位置設定為中心點並自動顯示地區範圍只有定位到當前位置後
mapView:DidUpdateUserLocation:
代理方法才會調用
- (void)viewDidLoad { [super viewDidLoad]; //擷取定位服務授權 [self requestUserLocationAuthor]; //初始化MKMapView [self initMapView];}- (void)requestUserLocationAuthor{ //如果沒有獲得定位授權,擷取定位授權請求 self.locationM = [[CLLocationManager alloc] init]; if ([CLLocationManager locationServicesEnabled]) { if ([CLLocationManager authorizationStatus] != kCLAuthorizationStatusAuthorizedWhenInUse) { [self.locationM requestWhenInUseAuthorization]; } }}- (void)initMapView{ CGFloat x = 0; CGFloat y = 20; CGFloat width = self.view.frame.size.width; CGFloat height = self.view.frame.size.height; //建立MKMapView對象 MKMapView *mapView = [[MKMapView alloc] initWithFrame:CGRectMake(x, y, width, height)]; //設定地圖類型 mapView.mapType = MKMapTypeStandard; //設定使用者跟蹤模式 mapView.userTrackingMode = MKUserTrackingModeFollow; mapView.delegate = self; [self.view addSubview:mapView]; self.mapView = mapView;}#pragma mark - MKMapViewDelegate/* 更新使用者位置會調用 */- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation{ CLLocation *location = userLocation.location; CLLocationCoordinate2D coordinate = location.coordinate; NSLog(@"經度:%f,緯度:%f",coordinate.latitude,coordinate.longitude);}
4. 添加圖釘
MapKit沒有內建的圖釘,只有圖釘協議MKAnnotation
,我們需要自訂圖釘:
1. 建立一個繼承NSObject
的類
2. 實現MKAnnotation
協議
3. 必須建立一個屬性,用於儲存圖釘位置
@property (nonatomic) CLLocationCoordinate2D coordinate;
下面就是我簡單建立的LTAnnotation類:
#import #import @interface LTAnnotation : NSObject /* 必須建立的屬性 */@property (nonatomic) CLLocationCoordinate2D coordinate;/* 可選的屬性 */@property (nonatomic, copy) NSString *title;@property (nonatomic, copy) NSString *subtitle;/* 自訂的屬性 */@property (nonatomic, strong) UIImage *icon;@end@implementation LTAnnotation@end
下面是實際的使用:
- (void)viewDidLoad { [super viewDidLoad]; //請求定位授權 [self requestUserLocationAuthor]; //初始化MKMapView [self initMapView]; //添加圖釘 [self addAnnotationsToMapView];}- (void)addAnnotationsToMapView{ CLLocationCoordinate2D location1 = CLLocationCoordinate2DMake(22.54, 114.02); //建立圖釘 LTAnnotation *annotation = [[LTAnnotation alloc] init]; annotation.title = @"執著"; annotation.subtitle = @"執著哥開的店"; annotation.coordinate = location1; annotation.icon = [UIImage imageNamed:@"red"]; //添加圖釘 [self.mapView addAnnotation:annotation1];}
5. 自訂圖釘視圖
上面的圖釘樣子是不是很醜,那是MKMapView
的預設樣式圖釘視圖MKAnnotationView
,我們先來瞭解下它的常用屬性:
@property (nonatomic, strong) id annotation;/*< 圖釘資料 */@property (nonatomic, strong) UIImage *image;/*< 圖釘的表徵圖 */@property (nonatomic, readonly) NSString *reuseIdentifier;/*< 圖釘的唯一標示 */@property (nonatomic) CGPoint calloutOffset;/*< 彈出視圖的位移 */@property (nonatomic) BOOL selected;/*< 是否選中 */@property (nonatomic) BOOL canShowCallout;/*< 是否能點擊彈出視圖 */@property (nonatomic, strong) UIView *leftCalloutAccessoryView;/*< 彈出視圖左邊的視圖 */@property (nonatomic, strong) UIView *rightCalloutAccessoryView;/*< 彈出視圖右邊的視圖 */
下面是通過設定
MKAnnotationView
的屬性,自訂圖釘視圖:
/* 每當圖釘顯示在視覺化介面上時,就會調用該方法,使用者位置的藍色點也是個圖釘,也會調用 */- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id)annotation{ if ([annotation isKindOfClass:[LTAnnotation class]]) { LTAnnotation *annotationLT = (LTAnnotation *)annotation; //類似於UITableViewCell的重用機制,圖釘視圖也有重用機制 static NSString *key = @"AnnotationIdentifier"; MKAnnotationView *view = [self.mapView dequeueReusableAnnotationViewWithIdentifier:key]; if (!view) { view = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:key]; } //設定圖釘資料 view.annotation = annotation; //自訂圖釘預設是NO,表示不能彈出視圖,這裡讓圖釘可以點擊彈出視圖 view.canShowCallout = YES; //設定圖釘表徵圖 view.image = annotationLT.icon; //設定彈出視圖的左邊視圖 UIImage *leftImage = [UIImage imageNamed:@"cafeIcon"]; UIImageView *leftView = [[UIImageView alloc] initWithImage: leftImage]; leftView.bounds = CGRectMake(0, 0, 50, 50); view.leftCalloutAccessoryView = leftView; //設定彈出視圖的右邊視圖 UIImage *rightImage = [UIImage imageNamed:@"cafeRight"]; UIImageView *rightView = [[UIImageView alloc] initWithImage: rightImage]; rightView.bounds = CGRectMake(0, 0, 50, 50); view.rightCalloutAccessoryView = rightView; return view; } //返回nil,表示顯示預設樣式 return nil;}
四、擴充–自訂圖釘彈出詳情視圖
如果你去關注下一些地圖應用,會發現他們的彈出視圖和我們的完全不一樣,那是怎麼實現的呢?
實際上那不是彈出視圖,那是個圖釘,只是這個圖釘做得和彈出視圖很像而已。
實現思路:當點擊普通的圖釘時,移除地圖上其他的詳情圖釘,添加當前圖釘的詳情圖釘當普通圖釘取消選中時,移除地圖上所有的詳情圖釘在
mapView:viewForAnnotation:
方法中設定普通圖釘視圖和詳情圖釘視圖下面是實現的部分代碼【實現效果比較隨便,見諒】:
#pragma mark - 地圖控制項代理方法/* 顯示圖釘時調用,注意方法中的annotation參數是即將顯示的圖釘對象 */-(MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id)annotation{ //由於當前位置的標註也是一個圖釘,所以此時需要判斷,此代理方法返回nil使用預設圖釘視圖 if ([annotation isKindOfClass:[LTAnnotation class]]) { static NSString *key1 = @"AnnotationKey1"; MKAnnotationView *annotationView = [_mapView dequeueReusableAnnotationViewWithIdentifier:key1]; //如果緩衝池中不存在則建立 if (!annotationView) { annotationView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:key1]; annotationView.canShowCallout = NO;//不允許彈出視圖,但可以被選中 } //重新設定此類圖釘視圖的圖釘模型(因為有可能是從緩衝池中取出來的,位置是放到緩衝池時的位置) annotationView.annotation = annotation; annotationView.image = ((LTAnnotation *)annotation).icon;//設定圖釘視圖的圖片 return annotationView; }else if([annotation isKindOfClass:[LTCalloutAnnotation class]]){ static NSString *key2 = @"AnnotationCallOutKey2"; MKAnnotationView *calloutView = [_mapView dequeueReusableAnnotationViewWithIdentifier:key2]; //如果緩衝池中不存在則建立 if (!calloutView) { calloutView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:key2]; calloutView.canShowCallout = NO;//不允許彈出視圖,但可以被選中 } //對於作為彈出詳情視圖的自訂圖釘視圖無彈出互動功能,在其中可以自由添加其他視圖 calloutView.annotation = annotation; //設定詳情圖釘的位移位置 calloutView.centerOffset = CGPointMake(-50, -80); [self calloutAddSubView:calloutView]; return calloutView; } else { return nil; }}
上面我的LTCalloutAnnotation和LTAnnotation實際上是只是類名不同而已,屬性都一樣。
#pragma mark 添加彈出視圖的子控制項,這裡我就很隨便了,你可以搞得好看點- (void)calloutAddSubView:(MKAnnotationView *)calloutView{ //添加背景 UIView *background = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 60)]; background.backgroundColor = [UIColor whiteColor]; background.layer.borderWidth = 5; background.layer.borderColor = [UIColor blueColor].CGColor; [calloutView addSubview:background]; //添加圖片 UIImage *image = [UIImage imageNamed:@"cafeRight"]; UIImageView *imageView = [[UIImageView alloc] initWithImage:image]; imageView.frame = CGRectMake(5, 5, 50, 50); [calloutView addSubview:imageView]; //添加一個紅色方塊 UIView *subview = [[UIView alloc] initWithFrame:CGRectMake(60, 5, 35, 40)]; subview.backgroundColor = [UIColor redColor]; [calloutView addSubview:subview];}#pragma mark 選中圖釘時觸發//點擊一般的圖釘KCAnnotation時添加一個圖釘作為所點圖釘的彈出詳情視圖-(void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view{ if ([view.annotation isKindOfClass:[LTAnnotation class]]) { LTAnnotation *annotation = view.annotation; //點擊一個圖釘時移除其他彈出詳情視圖 [self removeCalloutAnnotation]; //添加詳情圖釘 LTCalloutAnnotation *callout = [[LTCalloutAnnotation alloc] init]; callout.icon = annotation.icon; callout.title = annotation.title; callout.subtitle = annotation.subtitle; callout.coordinate = annotation.coordinate; [self.mapView addAnnotation:callout]; }}#pragma mark 取消選中時觸發-(void)mapView:(MKMapView *)mapView didDeselectAnnotationView:(MKAnnotationView *)view{ [self removeCalloutAnnotation];}#pragma mark 移除所用詳情圖釘-(void)removeCalloutAnnotation{ [self.mapView.annotations enumerateObjectsUsingBlock:^(id obj,NSUInteger idx,BOOL *stop){ if ([obj isKindOfClass:[LTCalloutAnnotation class]]) { [_mapView removeAnnotation:obj]; } }];}