ABABABA

iOS开发 - 地图与定位

2017-06-21  本文已影响2179人  misakatao

定位

定位功能
CLLocationManager 工作流程
926110847675.png
// 1、请求在使用期间授权定位,需在Info.plist文件中加入字段:NSLocationWhenInUseUsageDescription
- (void)requestWhenInUseAuthorization;

// 2、请求总是授权定位,需在Info.plist文件中加入字段:NSLocationAlwaysUsageDescription
- (void)requestAlwaysAuthorization;
    delegate:设置代理

    distanceFilter:设置定位频率,每隔多少米定位一次

    desiredAccuracy:设置定位精度(kCLLocationAccuracy)
// 1、是否启用定位服务,通常如果用户没有启用定位服务可以提示用户打开定位服务 + (BOOL)locationServicesEnabled; // 2、定位服务授权状态,返回枚举类型: + (CLAuthorizationStatus)authorizationStatus;
// 1、开始定位追踪
- (void)startUpdatingLocation;
// 2、停止定位追踪
- (void)stopUpdatingLocation;
// 3、开始导航方向追踪
- (void)startUpdatingHeading;
// 4、停止导航方向追踪
- (void)stopUpdatingHeading;
// 1、定位失败 - (void)locationManager:(CLLocationManager *)manager
    didFailWithError:(NSError *)error;

// 2、位置发生改变后执行(第一次定位到某个位置之后也会执行)
- (void)locationManager:(CLLocationManager *)manager
  didUpdateLocations:(NSArray *)locations;

// 3、导航方向发生变化后执行
- (void)locationManager:(CLLocationManager *)manager
  didUpdateHeading:(CLHeading *)newHeading;

// 4、进入某个区域之后执行
- (void)locationManager:(CLLocationManager *)manager
  didEnterRegion:(CLRegion *)region;

// 5、走出某个区域之后执行
- (void)locationManager:(CLLocationManager *)manager
  didExitRegion:(CLRegion *)region;
CLLocationManager 定位代码示例
#import "ViewController.h"
// 定位框架
#import <CoreLocation/CoreLocation.h>

#define NSLOG(OBJECT)  NSLog(@"%@", OBJECT)

@interfaceViewController () <CLLocationManagerDelegate>
{
    CLLocationManager *_locationManager; /**< 定位服务 */
}

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // 1、判断设备是否开启定位服务
    if (![CLLocationManager locationServicesEnabled]) {
        // 弹框提示
        UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"温馨提示" message:@"您的设备暂未开启定位服务!" preferredStyle:UIAlertControllerStyleAlert];
        [alertController addAction:[UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:nil]];
        [self presentViewController:alertController animated:YES completion:nil];
        
        return;
    }
    // 2、初始化定位服务
    _locationManager = [[CLLocationManager alloc] init];
    
    // 3、请求定位授权
    // 请求在使用期间授权(弹框提示用户是否允许在使用期间定位),需添加NSLocationWhenInUseUsageDescription到info.plist
    [_locationManager requestWhenInUseAuthorization];
    // 请求在后台定位授权(弹框提示用户是否允许不在使用App时仍然定位),需添加NSLocationAlwaysUsageDescription添加key到info.plist
    [_locationManager requestAlwaysAuthorization];
    
    // 4、设置定位精度
    _locationManager.desiredAccuracy = kCLLocationAccuracyBest;
    
    // 5、设置定位频率,每隔多少米定位一次
    _locationManager.distanceFilter = 10.0;
    
    // 6、设置代理 _locationManager.delegate = self;
    
    // 7、开始定位
    // 注意:开始定位比较耗电,不需要定位的时候最好调用 [stopUpdatingLocation] 结束定位。
    [_locationManager startUpdatingLocation];
}


#pragma mark - CLLocationManagerDelegate methods

// 定位失败
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error {
    
    NSLOG(error.localizedDescription);
}

// 位置更新
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations {
    // 获取最新定位
    CLLocation *location = locations.lastObject;
    
    // 打印位置信息
    NSLog(@"精度:%.2f, 纬度:%.2f", location.coordinate.latitude, location.coordinate.longitude);
    // 停止定位
    [_locationManager stopUpdatingLocation];
}

@end
1、定位频率和定位精度并不应当越精确越好,需要视实际情况而定,因为越精确越耗性能,也就越费电。
2、定位成功后会根据设置情况频繁调用didUpdateLocations:方法,这个方法返回一组地理位置对象数组,每个元素是一个CLLocation,代表地理位置信息(包含经度、纬度、海报、行走速度等信息),之所以返回数组是因为有些时候一个位置点可能包含多个位置。
3、使用完定位服务后如果不需要实时监控应该立即关闭定位服务以节省资源。
4、除了提供定位功能,CLLocationManager还可以调用startMonitoringForRegion:方法对指定区域进行监控。

地理编码

除了提供位置跟踪功能之外,在定位服务中还包含CLGeocoder 类用于处理地理编码和逆地理编码(又叫反地理编码)功能。

地理编码:根据给定的位置(通常是地名)确定地理坐标(经、纬度)。

逆地理编码:可以根据地理坐标(经、纬度)确定位置信息(街道、门牌等)。
// 1、地理编码 
- (void)geocodeAddressString:(NSString *)addressString completionHandler:(CLGeocodeCompletionHandler)completionHandler;

// 2、逆地理编码
- (void)reverseGeocodeLocation:(CLLocation *)location completionHandler:(CLGeocodeCompletionHandler)completionHandler;

#import "ViewController.h"
#import <CoreLocation/CoreLocation.h>

@interface ViewController ()
{
    CLGeocoder *_geocoder;
}

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    _geocoder = [[CLGeocoder alloc] init];
    
    [self getCoordinateByAddress:@"成都"];
    
    [self getAddressByLatitude:39.54 longitude:116.28];
    
}

#pragma mark - 根据地名确定地理坐标 

- (void)getCoordinateByAddress:(NSString *)address {
    // 地里编码
    [_geocoder geocodeAddressString:address completionHandler:^(NSArray<CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error) {
        // 取得第一个地标,地标中存储了详细的地址信息
        // 注意:一个地名可能搜索出多个地址
        CLPlacemark *placemark   = placemarks.firstObject;
        // 位置 CLLocation *location     = placemark.location;
        // 区域 CLRegion *region         = placemark.region;
        // 详细地址信息字典
        NSDictionary *addressDic = placemark.addressDictionary;
        NSLog(@"\n位置:%@\n区域:%@\n详细信息:%@\n", location, region, addressDic);
        /*
         NSString *name                  = placemark.name;//地名
         NSString *thoroughfare          = placemark.thoroughfare;//街道
         NSString *subThoroughfare       = placemark.subThoroughfare;//街道相关信息,例如门牌等
         NSString *locality              = placemark.locality;// 城市
         NSString *subLocality           = placemark.subLocality;// 城市相关信息,例如标志性建筑
         NSString *administrativeArea    = placemark.administrativeArea;// 州
         NSString *subAdministrativeArea = placemark.subAdministrativeArea;//其他行政区域信息
         NSString *postalCode            = placemark.postalCode;//邮编
         NSString *ISOcountryCode        = placemark.ISOcountryCode;//国家编码
         NSString *country               = placemark.country;//国家
         NSString *inlandWater           = placemark.inlandWater;//水源、湖泊
         NSString *ocean                 = placemark.ocean;// 海洋
         NSArray *areasOfInterest        = placemark.areasOfInterest;//关联的或利益相关的地标
         */
    }];
}

#pragma mark - 根据坐标取得地名 

- (void)getAddressByLatitude:(CLLocationDegrees)latitude longitude:(CLLocationDegrees)longitude {
    // 反地理编码
    CLLocation *location = [[CLLocation alloc] initWithLatitude:latitude longitude:longitude];
    
    [_geocoder reverseGeocodeLocation:location completionHandler:^(NSArray<CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error) {
        if (error) {
            NSLog(@"%@", error.localizedDescription);
        }else {
            CLPlacemark *placemark=[placemarks firstObject];
            NSLog(@"详细信息:%@",placemark.addressDictionary);
        }
    }];
}

@end

地图

MKMapView 常用属性
    MKUserTrackingModeNone:不进行用户位置跟踪

    MKUserTrackingModeFollow:跟踪用户位置

    MKUserTrackingModeFollowWithHeading:跟踪用户位置并且跟踪用户前进方向
    MKMapTypeStandard:标准地图,一般情况下使用此地图即可满足

    MKMapTypeSatellite:卫星地图

    MKMapTypeHybrid:混合地图,加载最慢比较消耗资源

    MKMapTypeSatelliteFlyover:iOS9新特性,卫星地图,支持城市观光

    MKMapTypeHybridFlyover:iOS9新特性,混合地图,支持城市观光
MKMapView 常用方法
- (instancetype)initWithFrame:(CGRect)frame;
// 1、添加大头针,对应的有添加大头针数组
 - (void)addAnnotation:(id <MKAnnotation>)annotation;

// 2、删除大头针,对应的有删除大头针数组 
- (void)removeAnnotation:(id <MKAnnotation>)annotation;

// 3、设置地图显示区域,用于控制当前屏幕显示地图范围 
- (void)setRegion:(MKCoordinateRegion)region animated:(BOOL)animated; 

// 4、设置地图中心点位置 
- (void)setCenterCoordinate:(CLLocationCoordinate2D)coordinate animated:(BOOL)animated;

 // 5、将地理坐标(经纬度)转化为数学坐标(UIKit坐标)CGPoint 
- (CGPoint)convertCoordinate:(CLLocationCoordinate2D)coordinate toPointToView:(nullable UIView *)view; 

// 6、将数学坐标CGPoint转换为地理坐标 
- (CLLocationCoordinate2D)convertPoint:(CGPoint)point toCoordinateFromView:(nullable UIView *)view; 

// 7、从缓存池中取出大头针,类似于UITableView中取出UITableViewCell,为了进行性能优化而设计 
- (nullable MKAnnotationView *)dequeueReusableAnnotationViewWithIdentifier:(NSString *)identifier; 

// 8、选中指定的大头针
- (void)selectAnnotation:(id <MKAnnotation>)annotation animated:(BOOL)animated; 

// 9、取消选中指定的大头针
- (void)deselectAnnotation:(nullable id <MKAnnotation>)annotation animated:(BOOL)animated;
// 1、用户位置发生改变时触发(第一次定位到用户位置也会触发该方法) 
- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation; 

// 2、地图加载完成后触发 
- (void)mapViewDidFinishLoadingMap:(MKMapView *)mapView; 

// 3、显示大头针时触发,返回大头针视图,通常自定义大头针可以通过此方法进行 
- (nullable MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation; 

// 4、点击选中某个大头针时触发 
- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view; 

// 5、取消选中大头针时触发 
- (void)mapView:(MKMapView *)mapView didDeselectAnnotationView:(MKAnnotationView *)view; 

// 6、渲染地图覆盖物时触发 
- (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id <MKOverlay>)overlay
用户位置跟踪
大头针
926141006362.png
- (void)respondsToGesture:(UILongPressGestureRecognizer *)gesture { // 当长按手势开始时,添加一个标注数据源if (gesture.state == UIGestureRecognizerStateBegan) { // 获取用户长按手势在地图上的点CGPoint point = [gesture locationInView:self.mapView]; // 将地图上的点转化为经纬度 CLLocationCoordinate2D coordinate = [self.mapView convertPoint:point toCoordinateFromView:self.mapView]; // 创建一个标注数据源,这里使用系统标注数据源:MKPointAnnotation,如果要自定义,必须遵守<MKAnnotation>协议 MKPointAnnotation *annotation = [[MKPointAnnotation alloc] init]; // 配置标注数据源 annotation.coordinate = coordinate;
        annotation.title = @"标注位置";
        annotation.subtitle = [NSString stringWithFormat:@"经度:%.2f, 纬度:%.2f", coordinate.longitude, coordinate.latitude]; // 在地图上添加标注 [self.mapView addAnnotation:annotation];
    }
}

MKPinAnnotationView
MKPinAnnotationView为MKAnnotationView子类,其特有属性如下:

    pinTintColor:设置大头针前景色(iOS9新特性)

    animatesDrop:设置大头针凋零效果
    image:设置大头针图片
    selected:是否被选中状态
    annotation:大头针模型信息,包括标题、子标题、地理位置
    calloutOffset:点击大头针时弹出详情信息视图的偏移量
    leftCalloutAccessoryView:弹出详情左侧视图
    rightCalloutAccessoryView:弹出详情右侧视图
    canShowCallout:点击大头针是否显示标题、子标题内容等,注意如果在viewForAnnotation:方法中重新定义大头针默认情况是无法交互的,需要设其值为true。
1、这个代理方法的调用时机:每当有大头针显示到系统可视界面中时就会调用此方法返回一个大头针视图放到界面中,同时当前系统位置标注(也就是地图中蓝色的位置点)也是一个大头针,也会调用此方法,因此处理大头针视图时需要区别对待。
    2、类似于UITableView的代理方法,此方法调用频繁,开发过程中需要重复利用MapKit的缓存池将大头针视图缓存起来重复利用。
    3、自定义大头针默认情况下不允许交互,如果交互需要设置canShowCallout = true。
    4、如果代理方法返回nil则会使用默认大头针视图,需要根据情况设置。

- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation {
    // MKPinAnnotationView:MKAnnotationView子类,可设置大头针颜色pinTintColor(iOS9)
    // 由于当前位置的标注也是一个大头针,所以此时需要判断,此代理方法返回nil使用默认大头针视图
    if ([annotation isKindOfClass:[MKUserLocation class]]) {
        return nil;
        
    }
    static NSString * kMKPinAnnotationViewIdentifier = @"identifier";
    // 大头针重用
    MKPinAnnotationView *annotationView = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:kMKPinAnnotationViewIdentifier];
    if (!annotationView) {
        // 如果缓存池中不存在则新建
        annotationView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:kMKPinAnnotationViewIdentifier];
    }
    // 修改大头针视图
    // 重新设置此类大头针视图的大头针模型(因为有可能是从缓存池中取出来的,位置是放到缓存池时的位置)
    annotationView.annotation = annotation;
    // 设置大头针图片
    annotationView.image = [UIImage imageNamed:@"pin"];
    // 设置大头针凋零效果
    annotationView.animatesDrop = YES;
    // 允许用户交互点击(弹框显示标注详情)
    annotationView.canShowCallout = YES;
    // 定义详情视图偏移量
    annotationView.calloutOffset = CGPointMake(0, 1);
    // 设置大头针颜色
    annotationView.pinTintColor = [UIColor blueColor];
    // 自定义大头针详情右侧视图
    UIButton *navigationBtn = [UIButton buttonWithType:UIButtonTypeCustom];
    navigationBtn.bounds = CGRectMake(0, 0, 100, 60);
    navigationBtn.backgroundColor = [UIColor grayColor];
    [navigationBtn setTitle:@"导航" forState:UIControlStateNormal];
    annotationView.rightCalloutAccessoryView = navigationBtn;
    
    return annotationView;
}

导航

926181326921.gif
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control {
    // 导航
    // 异常处理,如果标注数据源标题为空,则直接retun。
    if ([view.annotation.title isKindOfClass:[NSNull class]]) {
        return;
        
    }
    // 创建两个PlaceMark
    // MKPlacemark,定位框架中地标类,封装了详细的地理信息
    MKPlacemark *currentPlaceMark = [[MKPlacemark alloc] initWithCoordinate:mapView.userLocation.coordinate addressDictionary:nil];
    MKPlacemark *destinationPlaceMark = [[MKPlacemark alloc] initWithCoordinate:((MKPointAnnotation *)view.annotation).coordinate addressDictionary:nil];
    // 创建两个MapItem
    MKMapItem *currentItem = [[MKMapItem alloc] initWithPlacemark:currentPlaceMark];
    MKMapItem *destinationItem = [[MKMapItem alloc] initWithPlacemark:destinationPlaceMark];
    // 创建导航请求
    MKDirectionsRequest *directionRequest = [[MKDirectionsRequest alloc] init];
    // 配置导航请求
    // 1、起点
    directionRequest.source = currentItem;
    // 2、终点
    directionRequest.destination = destinationItem;
    // 发送导航请求
    MKDirections *derections = [[MKDirections alloc] initWithRequest:directionRequest];
    // 获取导航路线
    [derections calculateDirectionsWithCompletionHandler:^(MKDirectionsResponse *response, NSError *error) {
        // 添加一条路线(这里不会显示,需要渲染路线)
        // MKRoute  路径信息
        // polyLine 路线
        [_mapView addOverlay:((MKRoute *)response.routes[0]).polyline];
        
    }];
    
    // 设置交通类型
    directionRequest.transportType = MKDirectionsTransportTypeAutomobile;
    // 计算被请求的交通时间信息
    [directions calculateETAWithCompletionHandler:^(MKETAResponse * _Nullable response, NSError * _Nullable error) {
        NSLog(@"预计进行时间:%.2f", response.expectedTravelTime);
    }];
    // 值得一提的是,即使实现了上述逻辑,导航路线也并不会被显示出来,此时需要实现如下方法,对路线进行渲染。
}

// 路线渲染
- (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id<MKOverlay>)overlay {
    // 初始化路线渲染器
    MKPolylineRenderer *renderer = [[MKPolylineRenderer alloc] initWithPolyline:overlay];
    // 设置路线宽度
    renderer.lineWidth = 2.0;
    // 设置路线颜色
    renderer.strokeColor = [UIColor redColor];
    return renderer;
}

系统自带地图导航

926181116920.gif

#import "ViewController.h"
#import <CoreLocation/CoreLocation.h>
#import <MapKit/MapKit.h>

@interface ViewController ()
{
    CLGeocoder *_geocoder;
}
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    _geocoder=[[CLGeocoder alloc]init];
    
    [self startNavigation];
}

- (void)startNavigation {
    //根据“成都市”地理编码
    [_geocoder geocodeAddressString:@"成都市" completionHandler:^(NSArray *placemarks, NSError *error) {
        //获取第一个地标
        CLPlacemark *clPlacemark1 = [placemarks firstObject];
        
        MKPlacemark *mkPlacemark1 = [[MKPlacemark alloc]initWithPlacemark:clPlacemark1];
        //注意地理编码一次只能定位到一个位置,不能同时定位,所在放到第一个位置定位完成回调函数中再次定位
        [_geocoder geocodeAddressString:@"重庆市" completionHandler:^(NSArray *placemarks, NSError *error) {
            // 获取第一个地标
            CLPlacemark *clPlacemark2 = [placemarks firstObject];
            
            MKPlacemark *mkPlacemark2 = [[MKPlacemark alloc]initWithPlacemark:clPlacemark2];
            NSDictionary *options = @{MKLaunchOptionsMapTypeKey : @(MKMapTypeStandard),
                                      MKLaunchOptionsDirectionsModeKey : MKLaunchOptionsDirectionsModeDriving};
            // MKMapItem *mapItem1 =[MKMapItem mapItemForCurrentLocation];
            //当前位置
            MKMapItem *mapItem1=[[MKMapItem alloc]initWithPlacemark:mkPlacemark1];
            
            MKMapItem *mapItem2=[[MKMapItem alloc]initWithPlacemark:mkPlacemark2];
            // 打开导航地图
            [MKMapItem openMapsWithItems:@[mapItem1, mapItem2] launchOptions:options];
            
        }];
    }];
}
@end
特别说明
    CLLocation:用于表示位置信息,包含地理坐标、海拔等信息,包含在CoreLoaction框架中。

    MKUserLocation:一个特殊的大头针,表示用户当前位置。

    CLPlacemark:定位框架中地标类,封装了详细的地理信息。

    MKPlacemark:类似于CLPlacemark,只是它在MapKit框架中,可以根据CLPlacemark创建MKPlacemark。
上一篇 下一篇

猜你喜欢

热点阅读