iOS9地图定位(LBS)

地图定位(Location Based Service)相关的Framework:CoreLocation.framework, MapKit.framework

定位功能实现

准备工作

  1. 首先在当前控制器导入: #import <CoreLocation/CoreLocation.h>
  2. 在iOS8之后要在info.plist加入请求授权key值之后才可以定位:NSLocationWhenInUseUsageDescription,NSLocationAlwaysUsageDescription

简单的一次性定位功能实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
- (void)viewDidLoad {
[super viewDidLoad];

//创建定位管理对象
CLLocationManager *manager = [[CLLocationManager alloc] init];
self.manager = manager;

manager.delegate = self;

//请求授权
[manager requestAlwaysAuthorization]; //请求始终授权,并且在后台的时候继续授权定位

//开始定位
[manager startUpdatingLocation];
}

#pragma mark - locationManagerDelegate

- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations{

//获取经纬度
CLLocationCoordinate2D coordinate = locations.firstObject.coordinate;

NSLog(@"经度:%f 纬度:%f",coordinate.longitude, coordinate.latitude);

// 如果只获取位置一次,则停止定位
[manager stopUpdatingLocation];
}

定位其它功能详解

  1. 刚开始定位的时候,代码方法有可能不会调用即没有定位,可以通过选择模拟器的菜单栏:debug - Location - 随便换着切换几个试试,最后选择 Custom-Location
  2. [manager requestAlwaysAuthorization]; 请求永久授权,app不管在前台还是后天都可以获取用户位置
  3. [manager requestWhenInUseAuthorization]; 请求授权,app只能在前台执行的时候可以获取用户位置。注意:如果你要测试此句代码,并且和上句代码属于同一项目当中;那么你必须要卸载掉你的app,重新授权使用
  4. manager.distanceFilter = 100; 距离筛选器,当距离超过规定值后调用代理方法,单位:米。注意:在使用这句代码的时候,更新位置的代理方法不能有[manager stopUpdatingLocation];否则会不起作用。在模拟器当中可以修改Custom Location当中的经纬度来测试效果
  5. manager.desiredAccuracy = kCLLocationAccuracyBestForNavigation; 期望精度,在进行GPS导航的时候,用最好的精度值 kCLLocationAccuracyBestForNavigation
  6. 用 requestWhenInUseAuthorization app在后台获取不了用户的位置的时候,通过开启一个Bool属性来在后台临时获取用户的位置信息 manager.allowsBackgroundLocationUpdates = true; 。注意:此属性在 ios9 后可用, 没有添加key值会报错:reason: 'Invalid parameter not satisfying: !stayUp || CLClientIsBackgroundable(internal->fClient)' ,为了防止报错需要添加key值,如下所示
1
2
3
4
5
6
7
8
9
10
标准用法:

if([UIDevice currentDevice].systemVersion.floatValue >= 9.0) {

manager.allowsBackgroundLocationUpdates = true;
}

添加的key值为:
数组key值:Required background modes
数组中item值 :App registers for location updates

示意图:

地理编码和反地理编码

  1. 地理编码:将地名转换成经纬度
  2. 反地理编码:将经纬度转换成地名

地理编码代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
if (self.placeName.text.length == 0) return;

//创建地理编码对象
CLGeocoder *coder = [[CLGeocoder alloc] init];

//地理编码
[coder geocodeAddressString:self.placeName.text completionHandler:^(NSArray<CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error) {

if (error) {
NSLog(@"%@",error);
return ;
}

for (CLPlacemark *pm in placemarks) {

NSLog(@"%@",pm.name);
}

}];

反地理编码代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

if (self.longtitudeTxf.text.length == 0 || self.latitudeTxf.text.length == 0) return;

CLGeocoder *geoCoder = [[CLGeocoder alloc] init];

//反地理编码
CLLocation *location = [[CLLocation alloc] initWithLatitude:self.latitudeTxf.text.floatValue longitude:self.longtitudeTxf.text.floatValue];
[geoCoder reverseGeocodeLocation:location completionHandler:^(NSArray<CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error) {

if (error) {
NSLog(@"%@",error);
return ;
}

for (CLPlacemark *pm in placemarks) {

NSLog(@"%@",pm.name);
}
}];

MKMapView基本使用

MKMapView控件是苹果官方提供的地图控件,是通过国内的高德地图来实现的

准备

  1. 必须要添加 MapKit.framework 框架
  2. 在使用到 MKMapView的地方导入头文件#import <MapKit/MapKit.h>,该头文件包含#import <CoreLocation/CoreLocation.h>

基本定位功能实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83

- (void)viewDidLoad {
[super viewDidLoad];

//设置地图的类型
/*
MKMapTypeStandard = 0, 标准样式
MKMapTypeSatellite, 卫星视图, 纯卫星视图,不包含地名标注
MKMapTypeHybrid, 鸟瞰图(混合)
*/
self.mapView.mapType = MKMapTypeStandard;

//开启实时交通 ios9
self.mapView.showsTraffic = true;

//设置定位
self.manager = [[CLLocationManager alloc] init];
//定位授权,需要设置key值
[self.manager requestAlwaysAuthorization];

self.mapView.delegate = self;
//用户跟踪模式
self.mapView.userTrackingMode = MKUserTrackingModeFollow;


}

#pragma mark - mapView delegate

- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation{

//设置默认值
userLocation.title = @"title";
userLocation.subtitle = @"subTitle";

//反地理编码
CLGeocoder *geoCoder = [[CLGeocoder alloc] init];
[geoCoder reverseGeocodeLocation:userLocation.location completionHandler:^(NSArray<CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error) {

if (error) {

NSLog(@"%@",error);
return ;
}

userLocation.title = placemarks.firstObject.locality;
userLocation.subtitle = placemarks.firstObject.name;

}];
}

#pragma mark - method

/// 显示用户当前的位置
- (IBAction)backUserLocation {

//经纬度跨度
MKCoordinateSpan span = MKCoordinateSpanMake(0.2, 0.2);

//设置当前显示的区域,直接赋值没有动画
[self.mapView setRegion:MKCoordinateRegionMake(self.mapView.userLocation.location.coordinate, span) animated:true];
}

/// 缩放地图
- (IBAction)scaleMap:(UIButton *)sender {

//获取原先的经纬度的跨度
CGFloat latitudeDelta = self.mapView.region.span.latitudeDelta;
CGFloat longitudeDelta = self.mapView.region.span.longitudeDelta;

if (sender.tag == 10) { //放大

latitudeDelta = latitudeDelta * 0.5;
longitudeDelta = longitudeDelta * 0.5;

}else { //缩小

latitudeDelta = latitudeDelta * 2;
longitudeDelta = longitudeDelta * 2;
}

[self.mapView setRegion:MKCoordinateRegionMake(self.mapView.userLocation.coordinate, MKCoordinateSpanMake(latitudeDelta, longitudeDelta)) animated:true];
}

地图添加大头针

  • MKAnnotationView 注释视图,没有图片,可以自定义
  • MKPinAnnotationView 大头针注释视图,系统的大头针图片,自定义图片之后不会显示

添加大头针Demo

ViewController

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
 #import "ViewController.h"
#import "WSAnnotation.h"
#import "WSAnnotationView.h"
#import "WSImageAnnotationView.h"

@interface ViewController ()<MKMapViewDelegate>

@property (weak, nonatomic) IBOutlet MKMapView *mapView;

@property (strong, nonatomic) CLLocationManager *manager;

@end

@implementation ViewController

- (void)viewDidLoad {
[super viewDidLoad];

self.mapView.delegate = self;
self.manager = [[CLLocationManager alloc] init];
[self.manager requestAlwaysAuthorization];
self.mapView.userTrackingMode = MKUserTrackingModeFollow;
//默认添加一个大头针
WSAnnotation *annotation = [[WSAnnotation alloc] init];
annotation.coordinate = CLLocationCoordinate2DMake(40.5, 116.8);
[self.mapView addAnnotation:annotation];

}

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{

//根据当前所点的位置添加大头针
WSAnnotation *annotation = [[WSAnnotation alloc] init];
annotation.coordinate = [self.mapView convertPoint:[touches.anyObject locationInView:self.mapView] toCoordinateFromView:self.mapView];
[self.mapView addAnnotation:annotation];

}

#pragma mark - mapView delegate

- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation{

WSImageAnnotationView *annotationView = [WSImageAnnotationView annotationViewWithMapView:mapView annotation:annotation];

return annotationView;

}

@end

WSAnnotation

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
 #import <MapKit/MapKit.h>

@interface WSAnnotation : NSObject <MKAnnotation>

@property (nonatomic, assign) CLLocationCoordinate2D coordinate;
@property (nonatomic, copy, nullable) NSString *title;
@property (nonatomic, copy, nullable) NSString *subtitle;

@end


#import "WSAnnotation.h"

@implementation WSAnnotation

- (void)setCoordinate:(CLLocationCoordinate2D)coordinate{

_coordinate = coordinate;

//设置默认值
_title = @"default";
_subtitle = @"default";

//反地理编码
CLGeocoder *geoCoder = [[CLGeocoder alloc] init];

CLLocation *location = [[CLLocation alloc] initWithLatitude:coordinate.latitude longitude:coordinate.longitude];

[geoCoder reverseGeocodeLocation:location completionHandler:^(NSArray<CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error) {

if (error) {

NSLog(@"%@",error);
return ;
}

CLPlacemark *pm = placemarks.firstObject;
_title = pm.locality;
_subtitle = pm.name;

}];
}

@end

WSAnnotationView

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
 #import <MapKit/MapKit.h>
#import "WSAnnotation.h"

@interface WSAnnotationView : MKPinAnnotationView

+ (nullable instancetype)annotationViewWithMapView:(nonnull MKMapView *)mapView annotation:(nonnull WSAnnotation *)annotation;

@end


#import "WSAnnotationView.h"

@implementation WSAnnotationView

+ (instancetype)annotationViewWithMapView:(MKMapView *)mapView annotation:(WSAnnotation *)annotation{

//如果模型为 MKUserLocation,那么大头针是系统用户大头针则不要自定义,返回空,然后系统自己去处理
if ([annotation isKindOfClass:NSClassFromString(@"MKUserLocation")]) {
return nil;
}

static NSString *ID = @"WSAnnotationView";

WSAnnotationView * annotationView = (WSAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:ID];

if (annotationView == nil) {

annotationView = [[WSAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:ID];
//设置大头针的颜色 iOS9
annotationView.pinTintColor = [UIColor blueColor];

//设置掉落效果
annotationView.animatesDrop = true;

//显示大头针的内容信息
annotationView.canShowCallout = true;

//设置左边的显示view
annotationView.leftCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeContactAdd];
//设置右边
annotationView.rightCalloutAccessoryView = [[UISwitch alloc] init];
//自定义设置图片
annotationView.image = [UIImage imageNamed:@"me"];
}

return annotationView;

}
@end

WSImageAnnotationView

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
 #import <MapKit/MapKit.h>
#import "WSAnnotation.h"

@interface WSImageAnnotationView : MKAnnotationView

+ (nullable instancetype)annotationViewWithMapView:(nonnull MKMapView *)mapView annotation:(nonnull WSAnnotation *)annotation;

@end

#import "WSImageAnnotationView.h"

@implementation WSImageAnnotationView

+ (instancetype)annotationViewWithMapView:(MKMapView *)mapView annotation:(WSAnnotation *)annotation{

//如果模型为 MKUserLocation,那么大头针是系统用户大头针则不要自定义,返回空,然后系统自己去处理
if ([annotation isKindOfClass:NSClassFromString(@"MKUserLocation")]) {
return nil;
}

static NSString *ID = @"WSAnnotationView";

WSImageAnnotationView * annotationView = (WSImageAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:ID];

if (annotationView == nil) {

annotationView = [[WSImageAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:ID];

//显示大头针的内容信息
annotationView.canShowCallout = true;

//设置左边的显示view
annotationView.leftCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeContactAdd];
//设置右边
annotationView.rightCalloutAccessoryView = [[UISwitch alloc] init];
//自定义设置图片
annotationView.image = [UIImage imageNamed:@"me"];
}

return annotationView;

}