CoreLocationManager使用
- 前言
iOS9.0系统 didUpdateLocations代理方法中, 速度, 航向获取不到, 以后版本没有问题
iOS10以上app需要定位服务,请先在infoplist中添加位置权限
- Privacy - Location When In Use Usage Description 需要时访问位置
- Privacy - Location Always Usage Description 一直访问位置
手动获取定位信息
BOOL enable = [CLLocationManager locationServicesEnabled];
if (enable) {
//手机定位权限未开启(针对全部应用)
NSLog(@"系统定位已开启");
} else {
NSLog(@"系统定位未开启");
UIAlertController * alertVC = [UIAlertController alertControllerWithTitle:@"提示" message:@"系统定位尚未打开,请到【设定-隐私】中手动打开" preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction * tipsAction = [UIAlertAction actionWithTitle:@"好的" style:UIAlertActionStyleCancel handler:nil];
[alertVC addAction:tipsAction];
[self presentViewController:alertVC animated:YES completion:nil];
}
NSInteger state = [CLLocationManager authorizationStatus];
switch (state) {
case kCLAuthorizationStatusDenied:
{
NSLog(@"定位服务开启,被拒绝");
UIAlertController * alertVC = [UIAlertController alertControllerWithTitle:@"提示" message:@"app定位权限尚未打开, 不能获取附近信息" preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction * tipsAction = [UIAlertAction actionWithTitle:@"打开" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
//跳转到app的位置权限设置界面
NSURL*url = [NSURL URLWithString:UIApplicationOpenSettingsURLString];
if([[UIApplication sharedApplication] canOpenURL:url]) {
[[UIApplication sharedApplication] openURL:url];
}
}];
[alertVC addAction:tipsAction];
UIAlertAction * tipsAction1 = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:nil];
[alertVC addAction:tipsAction1];
[self presentViewController:alertVC animated:YES completion:nil];
break;
}
case kCLAuthorizationStatusNotDetermined:
NSLog(@"用户还未决定授权");
break;
case kCLAuthorizationStatusRestricted:
NSLog(@"授权定位服务, 未知原因");
break;
case kCLAuthorizationStatusAuthorizedAlways:{
NSLog(@"一直获得授权");
break;
}
case kCLAuthorizationStatusAuthorizedWhenInUse:{
NSLog(@"使用时获得授权");
break;
}
default:
break;
}
开启定位功能
CoreLocationManager使用
//引入框架, 声明遵守协议
#import <CoreLocation/CoreLocation.h>
@interface ViewController ()<CLLocationManagerDelegate>
@property (nonatomic, strong) CLLocationManager *locationManager;
@end
- (CLLocationManager *)locationManager
{
if (_locationManager == nil) {
_locationManager = [[CLLocationManager alloc] init];
_locationManager.delegate = self;
// _lm.distanceFilter = 0.5; //每隔多远定位一次, 距离过滤
/**
kCLLocationAccuracyBestForNavigation // 最适合导航
kCLLocationAccuracyBest; // 最好的
kCLLocationAccuracyNearestTenMeters; // 10m
kCLLocationAccuracyHundredMeters; // 100m
kCLLocationAccuracyKilometer; // 1000m
kCLLocationAccuracyThreeKilometers; // 3000m
*/
//精确度越高, 耗电, 定位时间长
_locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation;//渴望的准确性
/** -------iOS8.0+定位适配-------- */
//在plist中增加NSLocationWhenInUseUsageDescription和NSLocationAlwaysAndWhenInUsageDescription,两个key
// 获得在使用中定位的授权
[_locationManager requestWhenInUseAuthorization];
//获得前后台定位授权, 这个方法包含上一个方法
[_locationManager requestAlwaysAuthorization];
//以上方法二选一 根据项目需求而定
}
// 允许后台获取用户位置(iOS9.0)
if (@available(iOS 9.0, *)) {
// 如果想要支持后台定位, 下面这个行代码必须写, 前提一定要勾选后台模式 location updates
_locationManager.allowsBackgroundLocationUpdates = YES;
} else {
// Fallback on earlier versions
}
//9.0之后才有的, 就是在一定时间内按照distanceFilter的枚举来一层一层来定位, 如果超时就走didFailWithError: 方法(注意: 这个方法必须得有, 没有报错)
if (@available(iOS 9.0, *)) {
// [_locationManager requestLocation];
} else {
// Fallback on earlier versions
}
}
return _locationManager;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
//开始定位
[self.locationManager startUpdatingLocation];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
//结束定位
[self.locationManager stopUpdatingLocation];
}
CLLocationManagerDelegate代理方法
// 代理返回经纬度等信息
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations
{
CLLocation *newLocation = [locations lastObject];
NSLog(@"%@", newLocation);
// 判空处理
if (newLocation.horizontalAccuracy < 0) {
UIAlertController * alertVC = [UIAlertController alertControllerWithTitle:@"提示" message:@"定位错误,请检查手机网络以及定位" preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction * tipsAction = [UIAlertAction actionWithTitle:@"好的" style:UIAlertActionStyleCancel handler:nil];
[alertVC addAction:tipsAction];
[self presentViewController:alertVC animated:YES completion:nil];
return;
}
// 获取定位经纬度
CLLocationCoordinate2D coor2D = newLocation.coordinate;
NSLog(@"纬度为:%f, 经度为:%f", coor2D.latitude, coor2D.longitude);
// 获取定位海拔高度
CLLocationDistance altitude = newLocation.altitude;
NSLog(@"高度为:%f", altitude);
// 获取定位水平精确度, 垂直精确度
CLLocationAccuracy horizontalAcc = newLocation.horizontalAccuracy;
CLLocationAccuracy verticalAcc = newLocation.verticalAccuracy;
NSLog(@"%f, %f", horizontalAcc, verticalAcc);
// 航向
CLLocationDirection course = newLocation.course;
NSLog(@"高度为:%f", course);
// 速度
CLLocationSpeed speed = newLocation.speed;
NSLog(@"速度为:%f", speed);
}
授权状态发生改变时调用
/**
* 授权状态发生改变时调用
* @param manager 位置管理者
* @param status 状态
*/
-(void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status
{
switch (status) {
// 用户还未决定
case kCLAuthorizationStatusNotDetermined:
{
NSLog(@"用户还未决定");
break;
}
// 访问受限
case kCLAuthorizationStatusRestricted:
{
NSLog(@"访问受限");
break;
}
// 定位关闭时和对此APP授权为never时调用
case kCLAuthorizationStatusDenied:
{
// 定位是否可用(是否支持定位或者定位是否开启)
if([CLLocationManager locationServicesEnabled]) {
NSLog(@"app定位开启,但被用户拒绝");
} else {
NSLog(@"手机硬件定位关闭");
}
// NSLog(@"被拒");
break;
}
// 获取前后台定位授权
case kCLAuthorizationStatusAuthorizedAlways:
// case kCLAuthorizationStatusAuthorized: // 失效,不建议使用
{
NSLog(@"获取前台和后台双定位");
break;
}
// 获得前台定位授权
case kCLAuthorizationStatusAuthorizedWhenInUse:
{
NSLog(@"获得前台定位授权");
break;
}
default:
break;
}
}
地理编码
- 地理编码, 地名得到经纬度
//地名得到经纬度
- (IBAction)geocodeQuery:(id)sender {
CLGeocoder *geocode = [[CLGeocoder alloc] init];
[geocode geocodeAddressString:@"杭州" completionHandler:^(NSArray<CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error) {
CLPlacemark *placemark = [placemarks firstObject];
//得到的城市名字
self.textView.text = placemark.name;
self.txtLng.text = @(placemark.location.coordinate.longitude).stringValue;// 经度
self.txtlat.text = @(placemark.location.coordinate.latitude).stringValue;//纬度
}];
}
- 反地理编码, 经纬度得到地名
//经纬度得到地名
- (IBAction)reverseGeocode:(id)sender {
CLGeocoder *geocode = [[CLGeocoder alloc] init];
// 创建CLLocation对象
CLLocation *location = [[CLLocation alloc] initWithLatitude:[self.txtlat.text doubleValue] longitude:[self.txtLng.text doubleValue]];
// 根据CLLocation对象进行反地理编码
[geocode reverseGeocodeLocation:location completionHandler:^(NSArray<CLPlacemark *> * __nullable placemarks, NSError * __nullable error) {
// 包含区,街道等信息的地标对象
CLPlacemark *placemark = [placemarks firstObject];
// 城市名称
// NSString *city = placemark.locality;
// 街道名称
// NSString *street = placemark.thoroughfare;
// 全称
NSString *name = placemark.name;
self.textView.text = [NSString stringWithFormat:@"%@", name];
}];
}
插大头针
1>. 首先我们要创建一个大头针的数据模型, 继承NSObject遵守<MKAnnotation>协议
// MyAnnotation.h
#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>
@interface MyAnnotation : NSObject<MKAnnotation>
//地理坐标// 用readWrite修饰也行
@property (nonatomic, assign) CLLocationCoordinate2D coordinate;
@property (nonatomic, copy) NSString *title;
@property (nonatomic, copy) NSString *subtitle;
*以上三条是从代理复制过来的
//街道信息
@property (nonatomic, strong) NSString *streetAddress;
//城市信息
@property (nonatomic, strong) NSString *city;
//州, 省市信息
@property (nonatomic, strong) NSString *state;
- (instancetype)initWithCoordinate:(CLLocationCoordinate2D)coordinate;
@end
// MyAnnotation.m
#import "MyAnnotation.h"
@implementation MyAnnotation
- (instancetype)initWithCoordinate:(CLLocationCoordinate2D)coordinate
{
self = [super init];
if (self) {
self.coordinate = coordinate;
}
return self;
}
@end
2>. 将大头针数据模型添加到mapView上
举例1: 手指点击屏幕时, 获取屏幕坐标对应在地图中的经纬度, 来插大头针, 然后在大头针的标注中显示位置信息
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
//移除其他大头针
[self.mapView removeAnnotations:self.mapView.annotations];
//在touchBegan中获取手指点击屏幕坐标
CGPoint point = [[touches anyObject] locationInView:self.mapView];
//我们利用mapView的方法来, 利用屏幕坐标point得到mapView地图中的经纬度
CLLocationCoordinate2D coordinate = [self.mapView convertPoint:point toCoordinateFromView:self.mapView];
//来初始化一个地理编码类
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) {
// 就是取数组最后一个, [placemarks lastObiect]一样
for (CLPlacemark *placemark in placemarks) {
MyAnnotation *annotation = [[MyAnnotation alloc] initWithCoordinate:coordinate];//终于创建了大头针了
annotation.title = placemark.locality;
annotation.subTitle = placemark.administrativeArea;
annotation.zip = placemark.postalCode;
//添加大头针(注: 下面解释3. 大头针的代理方法)
[self.mapView addAnnotation:annotation];
}
if ([placemarks count] > 0) {
//设置当前手机地图的可视范围, 跟跨度差不多, 但跨度是角度, 东西, 南北跨度, 这个直接写多少米
MKCoordinateRegion viewReign = MKCoordinateRegionMakeWithDistance(coordinate, 10000, 10000);
[self.mapView setRegion:viewReign];
}
}];
//当添加[self.mapView addAnnotation: annotation]时系统会回调委托协议MKMapViewDelegate
— (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation方法, 我们需要实现这个方法, 才能实现在地图上添加标注的操作
例子2. 我们查询一个城市名字, 然后插个大头针子在地图上
[geocoder geocodeAddressString:@"杭州站" completionHandler:^(NSArray<CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error) {
for (CLPlacemark *placemark in placemarks) {
MyAnnotation *annotation = [[MyAnnotation alloc] initWithCoordinate:placemark.location.coordinate];
annotation.city = placemark.locality;
annotation.state = placemark.administrativeArea;
annotation.zip = placemark.postalCode;
annotation.title = annotation.city;
annotation.subtitle = annotation.state;
[self.mapView addAnnotation:annotation];
}
if ([placemarks count] > 0) {
MKPlacemark *lastPlacemark = (MKPlacemark *)placemarks.lastObject;
//这个方法可以设置mapView的显示范围(center, 米, 米)
MKCoordinateRegion viewReign = MKCoordinateRegionMakeWithDistance(lastPlacemark.location.coordinate, 100, 100);
[self.mapView setRegion:viewReign];
}
}];
3>. 大头针的代理方法
//当我们调用 [self.mapView addAnnotation:annotation];方法会来到这个方法
1. 使用系统大头针做的一些改变
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation
{
//MKPinAnnotationView是系统默认的大头针数据模型 , 这个跟tableView的重用的方式一样
MKPinAnnotationView *annotationView = (MKPinAnnotationView *)[self.mapView dequeueReusableAnnotationViewWithIdentifier:@"PIN_ANNOTATION"];
if (annotationView == nil) {
annotationView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:@"PIN_ANNOTATION"];
}
//是否弹出标注
annotationView.canShowCallout = true;
//设置大头针颜色
annotationView.pinTintColor = [UIColor blackColor];
//冲天而降
annotationView.animatesDrop = true;
return annotationView;
}
2. 自定义的大头针
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation
{
static NSString *ID = @"PIN_ANNOTATION";
MKAnnotationView *annotationView = (MKAnnotationView *)[self.mapView dequeueReusableAnnotationViewWithIdentifier:ID];
if (annotationView == nil) {
annotationView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:ID];
}
//必须设置, 默认是不弹出标注
annotationView.canShowCallout = true;
//设置大头针为图片
annotationView.image = [UIImage imageNamed:@"category_5"];
UIImageView *iv = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 44, 44)];
iv.image = [UIImage imageNamed:@"htl"];
annotationView.leftCalloutAccessoryView = iv;//标注的辅助视图
annotationView.rightCalloutAccessoryView = iv;//标注的辅助视图
annotationView.detailCalloutAccessoryView = [UISwitch new];//标注的辅助视图
return annotationView;
}
//大头针选中的代理方法, 里面有对应的大头针的数据模型
- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view
{
NSLog(@"纬度:%f----经度:%f", view.annotation.coordinate.latitude, view.annotation.coordinate.longitude);
}
- (void)mapView:(MKMapView *)mapView didDeselectAnnotationView:(MKAnnotationView *)view
{
NSLog(@"取消选中");
}
4>. 其他
// 3D视角
// MKMapCamera *camer = [MKMapCamera cameraLookingAtCenterCoordinate:CLLocationCoordinate2DMake(23.132931, 113.375924) fromEyeCoordinate:CLLocationCoordinate2DMake(23.135931, 113.375924) eyeAltitude:10];
// self.mapView.camera = camer;
//地图快照截图
MKMapSnapshotOptions *option = [[MKMapSnapshotOptions alloc] init];
// 针对地图
option.region = self.mapView.region;
option.showsBuildings = YES;
// 输出图片
option.size = CGSizeMake(1000, 2000);
option.scale = [UIScreen mainScreen].scale;
MKMapSnapshotter *snap = [[MKMapSnapshotter alloc] initWithOptions:option];
[snap startWithCompletionHandler:^(MKMapSnapshot * _Nullable snapshot, NSError * _Nullable error) {
if (error == nil) {
UIImage *image = snapshot.image;
NSData *data = UIImagePNGRepresentation(image);
[data writeToFile:@"/Users/........" atomically:YES];
}else
{
NSLog(@"--%@", error.localizedDescription);
}
}];
5>. 利用ios自带地图导航
// ViewController.m
// 导航
//
// Created by JasonBourne on 2018/5/4.
// Copyright © 2018年 JasonBourne. All rights reserved.
//
#import "ViewController.h"
#import <MapKit/MapKit.h>
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
MKLocalSearchRequest *request = [[MKLocalSearchRequest alloc] init];
request.naturalLanguageQuery = @"荷兰";
MKLocalSearch *search= [[MKLocalSearch alloc] initWithRequest:request];
[search startWithCompletionHandler:^(MKLocalSearchResponse * _Nullable response, NSError * _Nullable error) {
MKMapItem *lastmapItem = [response.mapItems lastObject];
[lastmapItem openInMapsWithLaunchOptions:@{
// 导航方式
MKLaunchOptionsDirectionsModeKey : MKLaunchOptionsDirectionsModeDriving,
// 地图类型
MKLaunchOptionsMapTypeKey : @(MKMapTypeHybrid),
// 是否显示交通
MKLaunchOptionsShowsTrafficKey : @(YES)
}];
}];
}