iOS原生地图基础(一)
2016-12-29 17:38
267 查看
在前两篇博客 iOS关于地图定位基础(一)和 iOS关于地图定位基础(二)里我们主要总结了在
iOS 中利用原生CoreLocation 框架来实现地理定位、编码、区域监听等简单的功能。但是要想在 iOS 中展示地图、以及添加可视化元素就必须用到另一个原生框架MapKit。接下来我们先简单了解和学习一下这个框架。
MapKit 框架中有一个核心类MKMapView,它是展示地图的一个控件,我们想要初始化这个控件必须导入框架头文件(通过SB来初始化地图控件的话,需要在项目中手动导入MapKit.framework)
: #import <MapKit/MapKit.h>// 导入框架主头文件。接下来看一下具体纯代码实例化地图和选择地图类型 :
(一、实例化地图和选择地图类型)
效果图如下 :
(二、显示用户自己的位置) 用到的MKMapView 的核心API :
但是因为 iOS 8 之后苹果加强了用户隐私保护,所以只要进行定位,就必须先授权。(具体授权方法前两篇博客已经讲过,代码如下)
(三、实现自动追踪用户位置 :)
示例图如下 :
(四、MKMapView 常用的代理)
① 如下代码,包含手动追踪用户位置、手动定义显示地图比例 (同时关闭自动追踪用户位置属性)
(五、MKMapView 添加大头针)
首先在上面的代理方法中认识到了一个新类 MKUserLocation,我们一般把这个对象叫做大头针模型,实际上这个类只是继承自 NSObject 类的一个普通类,唯独不同的是遵循了 MKAnnotation 协议。先看看下面添加大头针的API,这个方法是 MKMapView 的实例方法 :
大头针模型到底是干什么的呢?我们先来看看这个大头针模型协议 :
鉴于MKUserLocation 这个系统
4000
提供的大头针模型类的坐标属性、title属性等皆为只读,所以我们想自己创建大头针只能自定义大头针模型类,实现大头针模型协议中对应的方法。(在MKAnnotation 协议中出现属性,目的在于让遵循此协议的类自己实现对应的getter和setter方法的实现,和生成对应的成员变量,所以自定义大头针模型遵循协议后,直接在头文件当中重写属性即可。)自定义大头针模型代码如下 :
大头针模型有了之后,我们具体看下添加大头针代码(业务需求:点击地图哪个位置就添加一个大头针,同时利用地理反编码把对应大头针位置的城市显示出来):
(六、MKMapView 系统大头针)
我们在每次添加大头针时,都会调用系统 MKMapView 的对应代理方法 :
(七、MKMapView 自定义大头针)
在开发过程中我们也经常自定义大头针视图,由于不是系统大头针视图,所以可以任意更改大头针视图的图片样式。具体效果如下 :
具体代码如下 :
iOS 中利用原生CoreLocation 框架来实现地理定位、编码、区域监听等简单的功能。但是要想在 iOS 中展示地图、以及添加可视化元素就必须用到另一个原生框架MapKit。接下来我们先简单了解和学习一下这个框架。
MapKit 框架中有一个核心类MKMapView,它是展示地图的一个控件,我们想要初始化这个控件必须导入框架头文件(通过SB来初始化地图控件的话,需要在项目中手动导入MapKit.framework)
: #import <MapKit/MapKit.h>// 导入框架主头文件。接下来看一下具体纯代码实例化地图和选择地图类型 :
(一、实例化地图和选择地图类型)
#import "ViewController.h" #import <MapKit/MapKit.h> // 导入头文件 @interface ViewController () // 地图 @property (nonatomic, weak) MKMapView * mainMapView; @end @implementation ViewController // 地图视图懒加载 - (MKMapView *)mainMapView { if (!_mainMapView) { // 初始化地图 MKMapView *mainMapView = [[MKMapView alloc] initWithFrame:[UIScreen mainScreen].bounds]; _mainMapView = mainMapView; [self.view addSubview:_mainMapView]; } return _mainMapView; } - (void)viewDidLoad { [super viewDidLoad]; // 显示地图 [self mainMapView]; } - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { // 修改地图类型 self.mainMapView.mapType = MKMapTypeHybridFlyover; // 对应的枚举类型 typedef NS_ENUM(NSUInteger, MKMapType) { MKMapTypeStandard = 0, // 默认类型 MKMapTypeSatellite, // 卫星云图 MKMapTypeHybrid, // 云图 + 默认 MKMapTypeSatelliteFlyover, // iOS 9 新增三维立体云图 MKMapTypeHybridFlyover // iOS 9 新增三维立体云图 + 默认 }; } @end
效果图如下 :
(二、显示用户自己的位置) 用到的MKMapView 的核心API :
// 进行当前用户定位 self.mainMapView.showsUserLocation = YES;
但是因为 iOS 8 之后苹果加强了用户隐私保护,所以只要进行定位,就必须先授权。(具体授权方法前两篇博客已经讲过,代码如下)
@interface ViewController () // 地图 @property (nonatomic, weak) MKMapView * mainMapView; // 定位管理者 @property (strong, nonatomic) CLLocationManager * clManager; @end @implementation ViewController // 定位管理者懒加载 - (CLLocationManager *)clManager { if (!_clManager) { _clManager = [[CLLocationManager alloc] init]; } return _clManager; }
// 获取定位授权 同时配置 Info.plist 文件对应的授权键值 [self.clManager requestWhenInUseAuthorization];效果如下 :
(三、实现自动追踪用户位置 :)
// 自动追踪用户位置 self.mainMapView.userTrackingMode = MKUserTrackingModeFollowWithHeading; typedef NS_ENUM(NSInteger, MKUserTrackingMode) { MKUserTrackingModeNone = 0, // 默认不自动跟踪显示用户位置 MKUserTrackingModeFollow, // 自动跟踪显示用户位置 MKUserTrackingModeFollowWithHeading // 自动跟踪显示用户位置和朝向 };
示例图如下 :
(四、MKMapView 常用的代理)
① 如下代码,包含手动追踪用户位置、手动定义显示地图比例 (同时关闭自动追踪用户位置属性)
- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation { // 手动实现追踪用户位置 [mapView setCenterCoordinate:userLocation.location.coordinate animated:YES]; // 自定义显示比例 MKCoordinateSpan span = MKCoordinateSpanMake(0.01, 0.01); MKCoordinateRegion region = MKCoordinateRegionMake(userLocation.location.coordinate, span); [mapView setRegion:region animated:YES]; }效果图如下 :
(五、MKMapView 添加大头针)
首先在上面的代理方法中认识到了一个新类 MKUserLocation,我们一般把这个对象叫做大头针模型,实际上这个类只是继承自 NSObject 类的一个普通类,唯独不同的是遵循了 MKAnnotation 协议。先看看下面添加大头针的API,这个方法是 MKMapView 的实例方法 :
- (void)addAnnotation:(id <MKAnnotation>)annotation; // 从参数可以看到,模型对象必须遵循 MKAnnotation 协议
大头针模型到底是干什么的呢?我们先来看看这个大头针模型协议 :
@protocol MKAnnotation <NSObject> // 大头针的坐标(决定大头针放在什么位置) @property (nonatomic, readonly) CLLocationCoordinate2D coordinate; @optional // 大头针的注释信息 @property (nonatomic, readonly, copy, nullable) NSString *title; // 大头针辅助注释 @property (nonatomic, readonly, copy, nullable) NSString *subtitle; - (void)setCoordinate:(CLLocationCoordinate2D)newCoordinate NS_AVAILABLE(10_9, 4_0); @end
鉴于MKUserLocation 这个系统
4000
提供的大头针模型类的坐标属性、title属性等皆为只读,所以我们想自己创建大头针只能自定义大头针模型类,实现大头针模型协议中对应的方法。(在MKAnnotation 协议中出现属性,目的在于让遵循此协议的类自己实现对应的getter和setter方法的实现,和生成对应的成员变量,所以自定义大头针模型遵循协议后,直接在头文件当中重写属性即可。)自定义大头针模型代码如下 :
#import <Foundation/Foundation.h> #import <MapKit/MapKit.h> @interface HWUserLocation : NSObject <MKAnnotation> @property (nonatomic, assign) CLLocationCoordinate2D coordinate; @property (nonatomic, copy, nullable) NSString *title; @property (nonatomic, copy, nullable) NSString *subtitle; @end
大头针模型有了之后,我们具体看下添加大头针代码(业务需求:点击地图哪个位置就添加一个大头针,同时利用地理反编码把对应大头针位置的城市显示出来):
#import "ViewController.h" #import <MapKit/MapKit.h> #import "HWUserLocation.h" @interface ViewController () <MKMapViewDelegate> // 地图 @property (weak, nonatomic) IBOutlet MKMapView *mainMapView; // 地理编码器 @property (strong, nonatomic) CLGeocoder * geocoder; @end @implementation ViewController - (CLGeocoder *)geocoder { if (!_geocoder) { _geocoder = [[CLGeocoder alloc] init]; } return _geocoder; } - (void)viewDidLoad { [super viewDidLoad]; } - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { // 获取在 MapView 上点击的点 CGPoint currentPoint = [[touches anyObject] locationInView:self.mainMapView]; // 将点击的点转换为坐标 CLLocationCoordinate2D annoCoordinate = [self.mainMapView convertPoint:currentPoint toCoordinateFromView:self.mainMapView]; // 添加大头针 [self addAnnotationWtihCoordinate:annoCoordinate]; } - (void)addAnnotationWtihCoordinate:(CLLocationCoordinate2D)coordinate { // 创建大头针 HWUserLocation * anno = [[HWUserLocation alloc] init]; anno.coordinate = coordinate; anno.title = @"未知城市"; anno.subtitle = @"未知区域"; [self.mainMapView addAnnotation:anno]; // 地理反编码 CLLocation * location = [[CLLocation alloc] initWithLatitude:anno.coordinate.latitude longitude:anno.coordinate.longitude]; [self.geocoder reverseGeocodeLocation:location completionHandler:^(NSArray<CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error) { CLPlacemark * placemark = placemarks.firstObject; anno.title = placemark.locality; anno.subtitle = placemark.subLocality; }]; }效果图如下 :
(六、MKMapView 系统大头针)
我们在每次添加大头针时,都会调用系统 MKMapView 的对应代理方法 :
- (nullable MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation;在学习这个方法时,可以依照 UITableViewCell 的数据源创建方法来研究,因为 MKPinAnnotationView 系统大头针视图也用到了循环利用机制。现在我们手动创建一个系统大头针,并且设置一些效果属性,代码如下 :
#pragma mark - MKMapViewDelegate // 添加大头针 就会调用 - (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation { // 从缓存池获取大头针视图 MKPinAnnotationView * pinView = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:@"pinView"]; // 如果为空 手动创建 if (nil == pinView) { pinView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:@"pinView"]; } // 重新复制模型 避免数据混乱 pinView.annotation = annotation; // 设置大头针显示注释属性 pinView.canShowCallout = YES; // 设置颜色 pinView.pinTintColor = [UIColor blackColor]; // 设置大头针添加动画 pinView.animatesDrop = YES; // 为大头针添加图片 但是系统大头针无效 pinView.image = [UIImage imageNamed:@"pin.png"]; return pinView; }效果如下 :
(七、MKMapView 自定义大头针)
在开发过程中我们也经常自定义大头针视图,由于不是系统大头针视图,所以可以任意更改大头针视图的图片样式。具体效果如下 :
具体代码如下 :
#import "ViewController.h" #import <MapKit/MapKit.h> #import "HWUserLocation.h" #import "HWAnnotationView.h" @interface ViewController () <MKMapViewDelegate> // 地图 @property (weak, nonatomic) IBOutlet MKMapView *mainMapView; // 地理编码器 @property (strong, nonatomic) CLGeocoder * geocoder; // 位置管理者 @property (strong, nonatomic) CLLocationManager * clManager; @end @implementation ViewController - (CLLocationManager *)clManager { if (!_clManager) { _clManager = [[CLLocationManager alloc] init]; if ([[UIDevice currentDevice].systemVersion doubleValue] >= 8.0) { [_clManager requestAlwaysAuthorization]; } } return _clManager; } - (CLGeocoder *)geocoder { if (!_geocoder) { _geocoder = [[CLGeocoder alloc] init]; } return _geocoder; } - (void)viewDidLoad { [super viewDidLoad]; // 获取定位授权 [self clManager]; // 设置代理 self.mainMapView.delegate = self; // 地位用户位置 self.mainMapView.showsUserLocation = YES; } - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { // 获取在 MapView 上点击的点 CGPoint currentPoint = [[touches anyObject] locationInView:self.mainMapView]; // 将点击的点转换为坐标 CLLocationCoordinate2D annoCoordinate = [self.mainMapView convertPoint:currentPoint toCoordinateFromView:self.mainMapView]; // 添加大头针 [self addAnnotationWtihCoordinate:annoCoordinate]; } - (void)addAnnotationWtihCoordinate:(CLLocationCoordinate2D)coordinate { // 创建大头针 HWUserLocation * anno = [[HWUserLocation alloc] init]; anno.identifier = @"zidingyi"; anno.coordinate = coordinate; anno.title = @"未知城市"; anno.subtitle = @"未知区域"; anno.image = [UIImage imageNamed:[NSString stringWithFormat:@"category_%zd", arc4random_uniform(5) + 1]]; [self.mainMapView addAnnotation:anno]; // 地理反编码 CLLocation * location = [[CLLocation alloc] initWithLatitude:anno.coordinate.latitude longitude:anno.coordinate.longitude]; [self.geocoder reverseGeocodeLocation:location completionHandler:^(NSArray<CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error) { CLPlacemark * placemark = placemarks.firstObject; anno.title = placemark.locality; anno.subtitle = placemark.thoroughfare; }]; } #pragma mark - MKMapViewDelegate // 添加大头针 就会调用 - (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(HWUserLocation<MKAnnotation>*)annotation { // 判断大头针类型 if ([annotation respondsToSelector:@selector(identifier)] && [annotation.identifier isEqualToString:@"zidingyi"]) { // 创建自定义大头针 HWAnnotationView * annotationView = (HWAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:@"zidingyi"]; if (nil == annotationView) { annotationView = [[HWAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:@"zidingyi"]; } // 刷新大头针模型数据源 annotationView.annoLocation = annotation; return annotationView; } return nil; } - (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation { [mapView setCenterCoordinate:userLocation.location.coordinate animated:YES]; MKCoordinateSpan span = MKCoordinateSpanMake(0.01, 0.01); MKCoordinateRegion region = MKCoordinateRegionMake(userLocation.location.coordinate, span); [mapView setRegion:region animated:YES]; }自定义大头针视图类代码 :
#import <MapKit/MapKit.h> #import "HWUserLocation.h" @interface HWAnnotationView : MKAnnotationView // 大头针模型 @property (strong, nonatomic) HWUserLocation * annoLocation; @end
#import "HWAnnotationView.h" @implementation HWAnnotationView - (void)setAnnoLocation:(HWUserLocation *)annoLocation { _annoLocation = annoLocation; self.annotation = annoLocation; // 允许显示注释视图 self.canShowCallout = YES; // 设置大头针图片 self.image = annoLocation.image; // 设置左边辅助注释视图 UIImageView * leftView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 40, 40)]; leftView.image = [UIImage imageNamed:@"lanyangyang"]; self.leftCalloutAccessoryView = leftView; // 设置右边辅助注释视图 UIImageView * rightView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 40, 40)]; rightView.image = [UIImage imageNamed:@"meiyangyang"]; self.rightCalloutAccessoryView = rightView; } @end自定义大头针模型类 :
#import <Foundation/Foundation.h> #import <MapKit/MapKit.h> @interface HWUserLocation : NSObject <MKAnnotation> // 大头针位置坐标 @property (nonatomic, assign) CLLocationCoordinate2D coordinate; // 大头针注释 @property (nonatomic, copy) NSString *title; // 大头针辅助注释 @property (nonatomic, copy) NSString *subtitle; // 大头针图片 @property (strong, nonatomic) UIImage * image; // 大头针标识 @property (nonatomic, copy) NSString * identifier; @end
相关文章推荐
- iOS原生地图
- iOS原生地图开发进阶——使用导航和附近兴趣点检索
- iOS原生地图开发进阶——使用导航和附近兴趣点检索
- iOS关于地图定位基础(一)
- iOS高德地图之基础地图。
- ios地图开发之-Bing Maps地图基础教程
- iOS原生的定位和地图小结
- iOS 原生地图 开发
- iOS-地理坐标转换,原生地图获取的原始坐标转换为地图真实坐标
- iOS 原生地图地理编码与反地理编码
- iOS在地图开发基础,有点乱,将就一下
- iOS原生地图划线
- iOS原生地图开发指南续——大头针与自定义标注
- iOS 跳转到地图后导航(高德地图,百度地图,腾讯地图,苹果手机原生的地图)
- iOS 硬件 地图- 基础
- iOS原生地图开发指南
- iOS原生地图开发进阶——使用导航和附近兴趣点检索
- iOS原生的定位与地图归纳
- iOS原生地图热点搜索和基本使用汇总
- iOS 原生地图地理编码与反地理编码(详解)