###一.概览 现在很多社交、电商、团购应用都引入了地图和定位功能,似乎地图功能不再是地图应用和导航应用所特有的。的确,有了地图和定位功能确实让我们的生活更加丰富多彩,极大的改变了我们的生活方式。例如你到了一个陌生的地方想要查找附近的酒店、超市等就可以打开软件搜索周边;类似的,还有很多团购软件可以根据你所在的位置自动为你推荐某些商品。总之,目前地图和定位功能已经大量引入到应用开发中。今天就和大家一起看一下iOS如何进行地图和定位开发。
###二.定位
-
要实现地图、导航功能,往往需要先熟悉定位功能,在iOS中通过Core Location框架进行定位操作。Core Location自身可以单独使用,和地图开发框架MapKit完全是独立的,但是往往地图开发要配合定位框架使用。在Core Location中主要包含了定位、地理编码(包括反编码)功能。
-
定位是一个很常用的功能,如一些地图软件打开之后如果用户允许软件定位的话,那么打开软件后就会自动锁定到当前位置,如果用户手机移动那么当前位置也会跟随着变化。要实现这个功能需要使用Core Loaction中CLLocationManager类。
###三.获取自身的位置和经纬度
1.新建项目,将ios的提供位置服务和地图服务的库加入到项目中 点项目名->Build Phases点开Link Binary With Libraries
2.MapKit这个个库加入到项目中,他是操作MKMapView的库
3.新建UIViewController 设置好相关属性约束。导入<CoreLocation/CoreLocation.h>和<MapKit/MapKit.h>并且还要让控制器类实现MKMapViewDelegate协议
#import "ViewController.h"
#import <MapKit/MapKit.h>
#import <CoreLocation/CoreLocation.h>
@interface ViewController ()<MKMapViewDelegate>
@property (weak, nonatomic) IBOutlet MKMapView *mapView;
@property (strong, nonatomic) CLLocationManager *locationManager;
@property (strong, nonatomic) CLGeocoder *geocoder;
@property (strong, nonatomic) void(^geocodeComplatHandle)(NSString *name);
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// 首先设置地图的样式
// 这里有3种常用的地图类型
// MKMapTypeStandard 平面地图
// MKMapTypeSatellite 卫星地图
// MKMapTypeHybrid 混合地图
self.mapView.mapType = MKMapTypeStandard;
// 是否显示用户位置
self.mapView.showsUserLocation = YES;
//* 授权定位用户的位置,需要在info.plist文件中添加(以下二选一,两个都添加默认使用NSLocationWhenInUseUsageDescription)
// NSLocationWhenInUseUsageDescription 允许在前台使用时获取GPS的描述
// NSLocationAlwaysUsageDescription 允许永远可获取GPS的描述
// 请求用户授权
if ([CLLocationManager instanceMethodForSelector:@selector(requestWhenInUseAuthorization)]) {
[self.locationManager requestWhenInUseAuthorization];
}
}
4.懒加载的定位方法
- (CLLocationManager *)locationManager {
if (!_locationManager) {
_locationManager = [[CLLocationManager alloc] init];
// 定位的精确度
/**
kCLLocationAccuracyBestForNavigation
kCLLocationAccuracyBest;
kCLLocationAccuracyNearestTenMeters;
kCLLocationAccuracyHundredMeters;
kCLLocationAccuracyKilometer;
kCLLocationAccuracyThreeKilometers;
*/
_locationManager.desiredAccuracy = kCLLocationAccuracyBest;
// 多远距离更新位置
_locationManager.distanceFilter = 10;
}
return _locationManager;
}
5.模拟器在运行的时候,可以自定义的设置其自身所在的位置。在Debug-location-Custom locationL
####注意:
-
定位频率和定位精度并不应当越精确越好,需要视实际情况而定,因为越精确越耗性能,也就越费电。
-
定位成功后会根据设置情况频繁调用
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
方法,这个方法返回一组地理位置对象数组,每个元素一个CLLocation代表地理位置信息(包含经度、纬度、海报、行走速度等信息),之所以返回数组是因为有些时候一个位置点可能包含多个位置。 -
使用完定位服务后如果不需要实时监控应该立即关闭定位服务以节省资源。
-
除了提供定位功能,
CLLocationManager
还可以调用startMonitoringForRegion:
方法对指定区域进行监控。
###四.MKMapViewDelegate协议方法
1.地图加载完成时调用
- (void)mapViewDidFinishLoadingMap:(MKMapView *)mapView {
// 输出用户位置经纬度
NSLog(@"%s %f, %f",__func__, mapView.userLocation.coordinate.longitude, mapView.userLocation.coordinate.latitude);
}
2.用户位置更新后调用
- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation {
//设置中心点
CLLocationCoordinate2D center = userLocation.location.coordinate;
// 设置跨度
MKCoordinateSpan span = {0.01,0.01};
// 设置地图显示的范围
[self.mapView setRegion:MKCoordinateRegionMake(center, span) animated:YES];
NSLog(@"用户位置发生改变");
}
###五.标注视图
- 要给地图添加标注,有个必须要定义的属性
CLLocationCoordinate2D coordinate
#pragma mark - 标注视图
- (void)longGestAction:(UILongPressGestureRecognizer *)longGest {
// 1、获取触摸点所在地图上的CGPoint坐标
CGPoint point = [longGest locationInView:self.mapView];
// 2、将地图所在触摸点的坐标 CGPoint 转为对应的经纬度 CLLocationCoordinate2D
CLLocationCoordinate2D coordinate2D = [self.mapView convertPoint:point toCoordinateFromView:self.mapView];
if (longGest.state == UIGestureRecognizerStateBegan) {
// 3、创建并添加一个标注视图到mapView上
MKPointAnnotation *annotation = [[MKPointAnnotation alloc] init];
annotation.title = @"XXXX";
annotation.coordinate = coordinate2D;
annotation.subtitle = [NSString stringWithFormat:@"%f, %f",annotation.coordinate.latitude, annotation.coordinate.longitude];
[self setGeocodeComplatHandle:^(NSString *locationName) {
annotation.title = locationName;
}];
[self geocoder:coordinate2D];
//4、将标注添加到地图上
[self.mapView addAnnotation:annotation];
}
}
- 添加了标注视图后调用的方法
- (void)mapView:(MKMapView *)mapView didAddAnnotationViews:(NSArray<MKAnnotationView *> *)views {
NSLog(@"添加了标注视图后调用");
}
- 添加标注视图时调用的方法,这里可以自定义标注视图
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation {
NSLog(@"viewForAnnotation");
// 判断是否是用户当前的位置标注
if (![annotation isKindOfClass:[MKPointAnnotation class]]) {
return nil;
}
static NSString *identifer = @"annotation";
MKAnnotationView *annatationView = [mapView dequeueReusableAnnotationViewWithIdentifier:identifer];
//大头针样式
// MKPinAnnotationView *annatationView = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:identifer];
if (!annatationView) {
annatationView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:identifer];
//annatationView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:identifer];
// 是否显示辅助标注视图
annatationView.canShowCallout = YES;
// 设置大头针的颜色,前提是为MKPinAnnotationView类型
// annatationView.pinTintColor = [UIColor orangeColor];
//设置大头针出现的动画,前提是为MKPinAnnotationView类型
//annatationView.animatesDrop = YES;
// 使用图片作为标注
annatationView.image = [UIImage imageNamed:@"icon"];
// 是否可以拖拽
annatationView.draggable = YES;
// 设置标注视图的偏移
annatationView.centerOffset = CGPointMake(0, -25);
// 设置辅助视图的偏移
annatationView.calloutOffset = CGPointMake(0, -10);
UIButton *leftView = [UIButton buttonWithType:UIButtonTypeCustom];
[leftView setImage:[UIImage imageNamed:@"头像"] forState:UIControlStateNormal];
leftView.tag = 100;
leftView.frame = CGRectMake(0, 0, 50, 50);
// 设置左边的辅助视图
annatationView.leftCalloutAccessoryView = leftView;
// 设置右边的辅助视图
annatationView.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeContactAdd];
}
return annatationView;
}
- 点击标注视图时调用的方法
// 点击标注视图时调用
- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view {
NSLog(@"didSelectAnnotationView");
}
- 点击左右辅助视图时调用的方法
(void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control {
NSLog(@"%@",control);
}
###六.地理编码
-
除了提供位置跟踪功能之外,在定位服务中还包含
CLGeocoder
类用于处理地理编码和逆地理编码(又叫反地理编码)功能。 -
地理编码:根据给定的位置(通常是地名)确定地理坐标(经、纬度)。
-
逆地理编码:可以根据地理坐标(经、纬度)确定位置信息(街道、门牌等)。
-
CLGeocoder最主要的两个方法
1.地理编码- (void)geocodeAddressString:(NSString *)addressString completionHandler:(CLGeocodeCompletionHandler)completionHandler;
2.逆地理编码- (void)reverseGeocodeLocation:(CLLocation *)location completionHandler:(CLGeocodeCompletionHandler)completionHandler;
#pragma mark - 地理位置编码
- (void)geocoder:(CLLocationCoordinate2D)coordinate2D {
if (!_geocoder) {
_geocoder = [[CLGeocoder alloc] init];
}
// 创建一个经纬度位置 CLLocation
CLLocation *location = [[CLLocation alloc] initWithLatitude:coordinate2D.latitude longitude:coordinate2D.longitude];
// 1、通过经纬度获取对应的地理位置,查询位置是一个异步操作
[_geocoder reverseGeocodeLocation:location completionHandler:^(NSArray<CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error) {
CLPlacemark *placemark = [placemarks lastObject];
NSLog(@"%@ %@ %@",placemark.name, placemark.thoroughfare,placemark.subThoroughfare);
// 将地理位置名称显示在标注视图上
if (self.geocodeComplatHandle) {
self.geocodeComplatHandle(placemark.name);
}
}];
/* 2、通地理位置名称获取对应的经纬度
[_geocoder geocodeAddressString:@"中国四川省成都市双流区" completionHandler:^(NSArray<CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error) {
CLPlacemark *placemark = placemarks[0];
NSLog(@"%f, %f",placemark.location.coordinate.latitude, placemark.location.coordinate.longitude);
}];
*/
}
###七.DEMO