一、地图开发介绍

从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之前,实现这个功能只需要:
  1. 设置用户跟踪模式
  2. mapView:DidUpdateUserLocation:代理方法中设置地图中心和显示范围
在iOS8之后,用法稍有不同:
  1. 必须按照前面的定位章节的,获取前台或者前后台的定位服务授权,下面是链接:
    iOS学习笔记19-地图(一)定位CoreLocation
  2. 不需要进行中心点的指定,默认会将当前位置设置为中心点并自动显示区域范围
  3. 只有定位到当前位置后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 <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;
}

四、扩展--自定义大头针弹出详情视图

如果你去关注下一些地图应用,会发现他们的弹出视图和我们的完全不一样,那是怎么实现的呢?

实际上那不是弹出视图,那是个大头针,只是这个大头针做得和弹出视图很像而已。

实现思路:
  1. 当点击普通的大头针时,移除地图上其他的详情大头针,添加当前大头针的详情大头针
  2. 当普通大头针取消选中时,移除地图上所有的详情大头针
  3. 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哈!这种实现是很低效的,每次都需要遍历所有的大头针,从中找到详情大头针,需要优化的地方很多,可以自己去想着优化。

iOS学习笔记20-地图(二)MapKit框架的更多相关文章

  1. iOS学习笔记13-网络(二)NSURLSession

    在2013年WWDC上苹果揭开了NSURLSession的面纱,将它作为NSURLConnection的继任者.现在使用最广泛的第三方网络框架:AFNetworking.SDWebImage等等都使用 ...

  2. iOS学习笔记(十二)——iOS国际化

    开发的移动应用更希望获取更多用户,走向世界,这就需要应用国际化,国际化其实就是多语言.这篇文章介绍Xcode4.5以后的国际化,包括应用名国际化和应用内容国际化.如果是Xcode4.5之前版本请参考. ...

  3. iOS学习笔记-地图MapKit入门

    代码地址如下:http://www.demodashi.com/demo/11682.html 这篇文章还是翻译自raywenderlich,用Objective-C改写了代码.没有逐字翻译,如有错漏 ...

  4. iOS学习笔记-精华整理

    iOS学习笔记总结整理 一.内存管理情况 1- autorelease,当用户的代码在持续运行时,自动释放池是不会被销毁的,这段时间内用户可以安全地使用自动释放的对象.当用户的代码运行告一段 落,开始 ...

  5. iOS学习笔记总结整理

    来源:http://mobile.51cto.com/iphone-386851_all.htm 学习IOS开发这对于一个初学者来说,是一件非常挠头的事情.其实学习IOS开发无外乎平时的积累与总结.下 ...

  6. IOS学习笔记48--一些常见的IOS知识点+面试题

      IOS学习笔记48--一些常见的IOS知识点+面试题   1.堆和栈什么区别? 答:管理方式:对于栈来讲,是由编译器自动管理,无需我们手工控制:对于堆来说,释放工作由程序员控制,容易产生memor ...

  7. [置顶] iOS学习笔记47——图片异步加载之EGOImageLoading

    上次在<iOS学习笔记46——图片异步加载之SDWebImage>中介绍过一个开源的图片异步加载库,今天来介绍另外一个功能类似的EGOImageLoading,看名字知道,之前的一篇学习笔 ...

  8. iOS学习笔记06—Category和Extension

    iOS学习笔记06—Category和Extension 一.概述 类别是一种为现有的类添加新方法的方式. 利用Objective-C的动态运行时分配机制,Category提供了一种比继承(inher ...

  9. iOS学习笔记-自己动手写RESideMenu

    代码地址如下:http://www.demodashi.com/demo/11683.html 很多app都实现了类似RESideMenu的效果,RESideMenu是Github上面一个stars数 ...

随机推荐

  1. mvc工作总结

    MVC的页面跳转方式(放在一般类): filterContext.Result = new RedirectResult("controller/action"); filterC ...

  2. UVA 1412 Fund Management (预处理+状压dp)

    状压dp,每个状态可以表示为一个n元组,且上限为8,可以用一个九进制来表示状态.但是这样做用数组开不下,用map离散会T. 而实际上很多九进制数很多都是用不上的.因此类似uva 1601 Mornin ...

  3. jQuery中ready方法的实现

    https://blog.csdn.net/major_zhang/article/details/80146674 先普及一下jquery.ready()和window.onload,window. ...

  4. WPF使用附加属性绑定,解决data grid列绑定不上的问题

    背景 需要对datagrid的列header添加自定义属性,然后绑定,并根据不同的列header绑定不同的值,传统的加扩展类太麻烦,而附加属性的特点更适用于这种场景. 1.xaml 代码 <Da ...

  5. springboot-i18n国际化

    简介 In computing, internationalization and localization are means of adapting computer software to di ...

  6. 业务系统中最核心的状态设计,异常 case. (系统设计)

    系统设计几方面 1. 具象: 几个角色 -- 用例 2. 具象: 边界模块 3. 具象: 实体模块 4. 抽象: 详细设计后,抽出公用的部分. 5. Status状态字段的设置和更改 系统设计中最核心 ...

  7. webAssmebly实现js数组排序 使用custom elements和Shadow DOM自定义组件

    直接上码了……………… .wat源码 (module (type $t0 (func (param i32 i32))) (type $t1 (func (result i32))) (type $t ...

  8. 【OS_Linux】三大文本处理工具之grep命令

    grep(global search regular expression(RE) and print out the line,整行搜索并打印匹配成功的行 语法:grep  [选项]   搜索词  ...

  9. React碰到v-if

    最近在重构公司老项目,由于本人以前的技术栈是vue, 换工作后发现现在公司的技术栈是react, 所以重构的过程是及其痛苦.加之项目又是几年前的老项目,不敢大改,比葫芦画瓢比比皆是.本文就介绍下遇到的 ...

  10. 解决zend studio代码无法自动提示的3个方法

    最近电脑重装,索性把用了好多年的老版本7.x 升级了,网上下载了一个12.x的破解版. 起初一切正常,等导入项目开始开发的时候发现PHP函数尽然没有提示,一脸懵逼! 经过多方查阅和尝试,现在分享3个解 ...