地图与定位的使用场景
- 导航
- 社交
- O2O
- 手游
- 室内定位
定位
用于定位的方式:GPS/wifi/蜂窝基站/iBeacon
可用于计算方向
CoreLocation.framework用于进行定位计算
定位权限
耗电
定位服务
Standard location service:标准定位服务(GPS/wifi/蜂窝基站/iBeacon)
Significant-change location service 通过基站进行定位服务
Region monitoring 用作区域检测
定位权限申请
主要应用的类:CLLocationManager
//判断定位服务是否被使用
+ (BOOL)locationServicesEnabled API_AVAILABLE(ios(4.0), macos(10.7));
//用户对这个app所使用的授权状态
+ (CLAuthorizationStatus)authorizationStatus API_AVAILABLE(ios(4.2), macos(10.7));
//申请定位服务,用户首次调用的时候会触发一个弹窗,让用户来选择是否允许
- (void)requestWhenInUseAuthorization API_AVAILABLE(ios(8.0)) API_UNAVAILABLE(macos);
- (void)requestAlwaysAuthorization API_AVAILABLE(ios(8.0)) API_UNAVAILABLE(macos) API_UNAVAILABLE(tvos);
//启动位置更新/停止位置更新
- (void)startUpdatingLocation API_AVAILABLE(watchos(3.0)) API_UNAVAILABLE(tvos);
- (void)stopUpdatingLocation;
CLLocationManager的一些属性
//位置变化更新的一个最小距离,当用户的移动大于这个距离才会更新
@property(assign, nonatomic) CLLocationDistance distanceFilter;
//定位精度
@property(assign, nonatomic) CLLocationAccuracy desiredAccuracy;
//表示用户当前的位置
@property(readonly, nonatomic, copy, nullable) CLLocation *location;
CLAuthorizationStatus这个授权状态的枚举
//用户还没有进行配置
kCLAuthorizationStatusNotDetermined = 0,
//受限制
kCLAuthorizationStatusRestricted,
//拒绝
kCLAuthorizationStatusDenied,
//始终允许定位
kCLAuthorizationStatusAuthorizedAlways API_AVAILABLE(macos(10.12), ios(8.0)),
//在前台的时候允许使用定位
kCLAuthorizationStatusAuthorizedWhenInUse API_AVAILABLE(ios(8.0)) API_UNAVAILABLE(macos)
定位权限获取
- 隐式获取权限
[locationManager startUpdatingLocation];
- 显示获取权限
if([locationManager respondsToSelector:@selector(requestWhenInUseAuthorization)])
{
[locationManager requestWhenInUseAuthorization];
}
判断是否已经决定了定位权限
if (![CLLocationManager locationServicesEnabled])
{
NSLog(@"Location Service is Off!");
return;
}
获得的定位权限是什么
+(BOOL)authorized
{
return [CLLocationManager authorizationStatus]==kCLAuthorizationStatusAuthorizedWhenInUse||CLLocationManager authorizationStatus]==kCLAuthorizationStatusAuthorizedAlways||[CLLocationManager authorizationStatus]==kCLAuthorizationStatusAuthorized;
}
+(BOOL)denined
{
return [CLLocationManager authorizationStatus]==kCLAuthorizationStatusDenied;
}
open system setting(引导用户打开定位设置项)
- (void)gotoSetting:(id)sender
{
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]];
}
CLLocationManagerDelegate
//返回定位权限或者位置变化
//状态发生变化
- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status API_AVAILABLE(ios(4.2), macos(10.7));
//发生错误时
- (void)locationManager:(CLLocationManager *)manager
didFailWithError:(NSError *)error;
//位置发生变化
- (void)locationManager:(CLLocationManager *)manager
didUpdateLocations:(NSArray *)locations API_AVAILABLE(ios(6.0), macos(10.9));
CLLocation表示某个位置的定位信息
CLPlacemark地标
定位-CLGeocoder 需要注意的点
- 地理编码(geocoder)
根据给定的位置(通常是地名)确定地理坐标(经、纬度)。 - 反地理编码(Reverse geocoder)
根据地理坐标(经、纬度)获得位置信息(城市,道路等)
模拟器定位调试
demo
1.添加CoreLocation.framework
2.添加info.plist 权限描述
3.在需要的类中添加#import
#import
@interface ViewController ()
{
CLLocationManager *_locationManager;
CLGeocoder *_geoCoder;
}
- (void)viewDidLoad{
//定位权限
_locationManager =[[CLLocationManager alloc] init];
_locationManager.delegate = self;
if (![CLLocationManager locationServicesEnabled])
{
NSLog(@"Location Service is Off!");
return;
}
//请求定位服务
[_locationManager requestWhenInUseAuthorization];
[self startMonitorLocation];
}
- (void)startMonitorLocation
{
//精确定位
_locationManager.desiredAccuracy = kCLLocationAccuracyBest;
//位置变化更新的一个最小距离,当用户的移动大于这个距离才会更新
_locationManager.distanceFilter = 50;
//开始定位
[_locationManager startUpdatingLocation];
}
- (void)stopMonitorLocation
{
[_locationManager stopUpdatingLocation];
}
判断用户是否拒绝了定位权限
#pragma mark - location delegate
- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status
{
//当判断用户拒绝这个定位权限时候,引导用户打开这个定位设置界面
if(status==kCLAuthorizationStatusDenied)
{
//这里是点击这个按钮跳转,可以通过别的形式引导用户跳转别的位置
UIButton *button = [[UIButton alloc] init];
[button setTitle:@"goto setting" forState:UIControlStateNormal];
[button setBackgroundColor:[UIColor orangeColor]];
[button setFrame:CGRectMake(100, 100, 150, 50)];
[button addTarget:self action:@selector(gotoSetting:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:button];
}
NSLog(@"CLAuthorizationStatus %zd", status);
}
#pragma mark - button
- (void)gotoSetting:(id)sender
{
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]];
}
获取定位数据
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
CLLocation *location = [locations firstObject];
//当前打印出来的是经纬度信息
NSLog(@"location updated! %@", [NSString stringWithFormat:@"latitude %f, longtitude %f", location.coordinate.latitude, location.coordinate.longitude]);
//用反地理编码将经纬度转成真实的地理信息
if (!_geoCoder) {
_geoCoder = [[CLGeocoder alloc] init];
}
[_geoCoder reverseGeocodeLocation:location completionHandler:^(NSArray * _Nullable placemarks, NSError * _Nullable error) {
//从placemark中可以取出地理城市,国家等信息
for (CLPlacemark *placemark in placemarks)
{
NSLog(@"name=%@,locality=%@ country=%@", placemark.name, placemark.locality, placemark.country);
}
}];
}
定位出错的时候加入出错处理
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
NSLog(@"location error");
switch ([error code]) {
case kCLErrorDenied:
[self stopMonitorLocation];
break;
case kCLErrorLocationUnknown:
[self stopMonitorLocation];
break;
default:
break;
}
}
typedef NS_ENUM(NSUInteger,MKMapType)
{
//标准地图
MKMapTypeStandard
//卫星地图
MKMapTypeStatellite
//标准和卫星混合模式
MKMapTypeHybrid
//3D立体地图
MKMapTypeStatelliteFlyover,
MKMapTypeHybridFlyover
}
typedef NS_ENUM(NSInteger, MKUserTrackingMode) {
MKUserTrackingModeNone = 0, // the user's location is not followed
//跟踪用户当前位置
MKUserTrackingModeFollow, // the map follows the user's location
//跟踪用户的朝向
MKUserTrackingModeFollowWithHeading __TVOS_PROHIBITED, // the map follows the user's location and heading
} NS_ENUM_AVAILABLE(NA, 5_0) __TVOS_AVAILABLE(9_2) __WATCHOS_PROHIBITED;
MKMapView
MKMapViewDelegate
//返回用户当前的位置
- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation NS_AVAILABLE(10_9, 4_0);
//错位就返回fail
- (void)mapView:(MKMapView *)mapView didFailToLocateUserWithError:(NSError *)error NS_AVAILABLE(10_9, 4_0);
Demo:展示用户当前的位置
1.设置mapview
//step1
_mapView = [[MKMapView alloc] init];
_mapView.frame = self.view.frame;
_mapView.delegate = self;
//设置为标准地图
_mapView.mapType = MKMapTypeStandard;
//地图上显示的信息
_mapView.showsUserLocation = YES;
_mapView.showsScale = YES;
_mapView.showsTraffic = YES;
_mapView.showsCompass = YES;
//跟踪位置并且记录前进方向
_mapView.userTrackingMode = MKUserTrackingModeFollowWithHeading;
self.view = _mapView;
//申请定位权限
_locationManager = [[CLLocationManager alloc] init];
[_locationManager requestWhenInUseAuthorization];
//反编码获取坐标对应地点
_geocoder = [[CLGeocoder alloc] init];
//点数据
_pointAnnotation = [[MKPointAnnotation alloc] init];
_pointAnnotation.title = @"奶茶 刘若英";
_pointAnnotation.subtitle = @"原来你也在这里 盛大开演";
CLLocationCoordinate2D coordinate;
coordinate.latitude = 30.189845;
coordinate.longitude = 120.187883;
_pointAnnotation.coordinate = coordinate;
[_mapView addAnnotation:_pointAnnotation];
self.availableMaps = [NSMutableArray array];
[self getAvailableMapsApps];
#pragma mark - mapview delegate
- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation
{
[_geocoder reverseGeocodeLocation:userLocation.location completionHandler:^(NSArray * _Nullable placemarks, NSError * _Nullable error) {
if (placemarks.count > 0)
{
CLPlacemark *placemark = placemarks[0];
userLocation.title = @"当前位置";
userLocation.subtitle = [NSString stringWithFormat:@"%@ %@", placemark.locality, placemark.thoroughfare];
}
}];
}
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id)annotation
{
if ([annotation isKindOfClass:[MKPointAnnotation class]])
{
NSString *pointAnnotationIdentifier = @"pointAnnotationIdentifier";
MKAnnotationView *annotationView = [_mapView dequeueReusableAnnotationViewWithIdentifier:pointAnnotationIdentifier];
if (!annotationView) {
annotationView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:pointAnnotationIdentifier];
UIButton *testButton = [[UIButton alloc] init];
[testButton setBackgroundColor:[UIColor orangeColor]];
[testButton setTitle:@"Go" forState:UIControlStateNormal];
testButton.frame = CGRectMake(0, 0, 50, 50);
[testButton addTarget:self action:@selector(go:) forControlEvents:UIControlEventTouchUpInside];
annotationView.rightCalloutAccessoryView = testButton;
}
annotationView.annotation = _pointAnnotation;
annotationView.canShowCallout = YES;
return annotationView;
}
return nil;
}
跳转到第三方导航登录
#pragma mark - button
- (void)go:(id)sender
{
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"选择导航" message:@"选择你要的导航服务" preferredStyle:UIAlertControllerStyleActionSheet];
UIAlertAction *action1 = [UIAlertAction actionWithTitle:@"系统导航" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
CLLocationCoordinate2D coordinateEnd = CLLocationCoordinate2DMake(31.18, 121.43);
MKPlacemark *placemark = [[MKPlacemark alloc] initWithCoordinate:coordinateEnd];
MKMapItem *toLocation = [[MKMapItem alloc] initWithPlacemark:placemark];
toLocation.name = @"目的地";
MKMapItem *currentLocation = [MKMapItem mapItemForCurrentLocation];
[MKMapItem openMapsWithItems:@[currentLocation, toLocation] launchOptions:@{
MKLaunchOptionsDirectionsModeKey:MKLaunchOptionsDirectionsModeDriving,
MKLaunchOptionsShowsTrafficKey:@YES
}];
}];
[alert addAction:action1];
for (NSDictionary *dic in self.availableMaps)
{
UIAlertAction *action2 = [UIAlertAction actionWithTitle:[NSString stringWithFormat:@"使用%@导航", dic[@"name"]] style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
NSString *string = dic[@"url"];
string = [string stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
NSURL *url = [NSURL URLWithString:string];
[[UIApplication sharedApplication] openURL:url];
}];
[alert addAction:action2];
}
UIAlertAction *action3 = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:nil];
[alert addAction:action3];
[self presentViewController:alert animated:YES completion:nil];
}
- (void)getAvailableMapsApps
{
[self.availableMaps removeAllObjects];
CLLocationCoordinate2D coordinateStart = CLLocationCoordinate2DMake(30.19, 120.19);
CLLocationCoordinate2D coordinateEnd = CLLocationCoordinate2DMake(30.23, 120.15);
if ([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"baidumap://"]])
{
NSString * urlString = [NSString stringWithFormat:@"baidumap://map/direction?origin=latlng:%f,%f|name:我的位置&destination=latlng:%f,%f|name:目的地&mode=driving",coordinateStart.latitude,coordinateStart.longitude,coordinateEnd.latitude,coordinateEnd.longitude];
NSDictionary *dic = @{@"name" : @"百度地图", @"url" : urlString};
[self.availableMaps addObject:dic];
}
}