###一、地圖開發介紹 從iOS6.0開始地圖資料不再由谷歌驅動,而是改用自家地圖,當然在國內它的資料是由高德地圖提供的。 ######在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 <MKAnnotation>)annotation;
- (void)addAnnotations:(NSArray<id<MKAnnotation>> *)annotations;
/* 刪除大頭針 */
- (void)removeAnnotation:(id <MKAnnotation>)annotation;
- (void)removeAnnotations:(NSArray<id<MKAnnotation>> *)annotations;
/* 選中大頭針與取消選中大頭針 */
- (void)selectAnnotation:(id <MKAnnotation>)annotation
animated:(BOOL)animated;
- (void)deselectAnnotation:(id <MKAnnotation>)annotation
animated:(BOOL)animated;
/* 獲取大頭針檢視 */
- (MKAnnotationView *)viewForAnnotation:(id <MKAnnotation>)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<MKAnnotation>)annotation;
複製程式碼
###三、MapKit使用 ####1. 首先新增標頭檔案:
#import <MapKit/MapKit.h>
複製程式碼
####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
,我們需要自定義大頭針:
- 建立一個繼承
NSObject
的類
- 實現
MKAnnotation
協議 - 必須建立一個屬性,用於儲存大頭針位置
@property (nonatomic) CLLocationCoordinate2D coordinate;
複製程式碼
######下面就是我簡單建立的LTAnnotation類:
#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>
@interface LTAnnotation : NSObject <MKAnnotation>
/* 必須建立的屬性 */
@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<MKAnnotation> 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<MKAnnotation>)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<MKAnnotation>)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];
}
}];
}
複製程式碼
這個自定義彈出詳情檢視,我做的比較簡陋,我主要是為了好說明具體是怎麼實現的,你可以把彈出介面做的好看點,順便把一些大頭針檢視進行下封裝,那一切就很完美了,O(∩_∩)O哈!這種實現是很低效的,每次都需要遍歷所有的大頭針,從中找到詳情大頭針,需要優化的地方很多,可以自己去想著優化。
#####如果有什麼意見請在下方評論區寫出來,求關注,求打賞,O(∩_∩)O哈!