您的位置:首页 > 移动开发 > IOS开发

iOS原生地图基础(一)

2016-12-29 17:38 267 查看
  在前两篇博客 iOS关于地图定位基础(一)和 iOS关于地图定位基础(二)里我们主要总结了在
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 地图 定位