你的位置:首页 > 操作系统

[操作系统]iOS学习48地图


一、地图的简介

 在移动互联网时代,移动app能解决用户的很多生活琐事,比如   

  导航:去任意陌生的地方

  周边:找餐馆、找酒店、找银行、找电影院

  手机软件:微信摇一摇、QQ附近的人、微博、支付宝等

 在上述应用中,都用到了地图和定位功能,在iOS开发中,要想加入这两大功能,必须基于两个框架进行开发

  MapKit :用于地图展示

  CoreLocation :用于地理定位

二、地图定位(CoreLocation框架,地理编码与反地理编码)

 1、CoreLocation框架的使用

  • 导入框架  (iOS5之后不再需要)

     ③ 请求授权
                requestWhenInUseAuthorization要和infoPlist文件中的匹配(NSLocationWhenInUseUsageDescription)
                requestAlwaysAuthorization要和infoPlist文件中的匹配(NSLocationAlwaysUsageDescription)

  // 第二步:进行隐私的判断和授权  // 进行隐私的判断  if (![CLLocationManager locationServicesEnabled]) {     NSLog(@"是否前往隐私进行设置允许定位");  }  // 进行版本的判断  if ([[[UIDevice currentDevice] systemVersion] integerValue] >= 8.0) {    // 根据状态进行授权    if ([CLLocationManager authorizationStatus] != kCLAuthorizationStatusAuthorizedWhenInUse) {            // 请求授权      [self.manager requestWhenInUseAuthorization];
       
       [self.manager requestWhenInUseAuthorization]; }
   }

  注意:从iOS 7之后,苹果在保护用户隐私方面做了很大的加强,以下操作都必须经过用户批准授权:

   ① 要想获得用户的位置和访问用户的通讯录、日历、相机、相册等等都需要用户来手动授权。

   ② 当想访问用户的隐私信息时,系统会自动弹出一个对话框让用户授权

  • 第三步:设置管理器的代理和相关属性
  // 第三步:设置管理器的代理和相关属性  self.manager.delegate = self;    // 设置精度  self.manager.desiredAccuracy = kCLLocationAccuracyHundredMeters;    // 设置最小更新距离  self.manager.distanceFilter = 10;

  CLLocationManagerDelegate

  定位成功后开始更新位置信息,只要移动的距离大于设置最小更新距离时也开始走这个方法:

- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations{   // 获取最后一次的位置  CLLocation *location = locations.lastObject;  // 获取位置坐标  CLLocationCoordinate2D coordinate = location.coordinate;  // 打印经纬度,longitude 经度 latitude 纬度  NSLog(@"经度:%f, 纬度:%f, 海拔:%f, 航向:%f, 行走速度:%f", coordinate.longitude, coordinate.latitude, location.altitude, location.course, location.speed);    // 为了节省电源,如果不使用定位,需要把定位关闭  [self.manager stopUpdatingLocation];}

   定位失败

- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error{  NSLog(@"定位失败");}

 6、地理编码与地理反编码

  • 使用CLGeocoder可以完成"地理编码"和"反地理编码"

   地理编码:根据给定的地名,获得具体的位置信息(比如经纬度、地址的全称等)

   反地理编码:根据给定的经纬度,获得具体的位置信息

  • 具体方法:
// 地理编码方法- (void)geocodeAddressString:(NSString *)addressString completionHandler:(CLGeocodeCompletionHandler)completionHandler;// 反地理编码方法- (void)reverseGeocodeLocation:(CLLocation *)location completionHandler:(CLGeocodeCompletionHandler)completionHandler;

  • CLGeocodeCompletionHandler

  当地理编码/反地理编码完成时,就会调用 CLGeocodeCompletionHandler,头文件声明:

typedef void (^CLGeocodeCompletionHandler)(NSArray<CLPlacemark *> *placemarks, NSError *error);

  block包含2个参数

   error:当编码出错时(比如编码不出具体的信息),2其错误信息会包含在error中

   placemarks:里面装着 CLPlacemark 对象

  • CLPlacemark

  CLPlacemark的字面意思是地标,封装详细的地址位置信息,相关属性:

// 地理位置@property (nonatomic, readonly) CLLocation *location;// 区域@property (nonatomic, readonly) CLRegion *region;// 详细的地址信息@property (nonatomic, readonly) NSDictionary *addressDictionary;// 地址名称@property (nonatomic, readonly) NSString *name;// 地点名称 @property (nonatomic, readonly) NSString *locality;

 7、详细实例代码

#import "ViewController.h"// 第一步:引入库的头文件#import <CoreLocation/CoreLocation.h>@interface ViewController () <CLLocationManagerDelegate>// CoreLocation框架中的CLLocationManager用于管理定位的管理器/// 定位管理器@property (nonatomic, strong) CLLocationManager *manager;/// 编码和反编码类@property (nonatomic, strong) CLGeocoder *geocoder;@end@implementation ViewController- (void)viewDidLoad {  [super viewDidLoad];  // Do any additional setup after loading the view, typically from a nib.    // 定位的步骤    // 第一步:初始化定位管理器  self.manager = [[CLLocationManager alloc] init];    // 第二步:进行隐私的判断和授权  // 进行隐私的判断  if (![CLLocationManager locationServicesEnabled]) {    NSLog(@"是否前往隐私进行设置允许定位");  }  // 进行版本的判断  if ([[[UIDevice currentDevice] systemVersion] integerValue] >= 8.0) {        // 根据状态进行授权       if ([CLLocationManager authorizationStatus] != kCLAuthorizationStatusAuthorizedWhenInUse) {      // 请求授权      [self.manager requestWhenInUseAuthorization];    }  }    // 第三步:设置管理器的代理和相关属性  self.manager.delegate = self;    // 设置精度  self.manager.desiredAccuracy = kCLLocationAccuracyHundredMeters;    // 设置最小更新距离  self.manager.distanceFilter = 10;    // 第四步:开启定位  [self.manager startUpdatingLocation];    /***********************编码和反编码**********************/    // 初始化对象  self.geocoder = [[CLGeocoder alloc] init];    // 根据地名获取经纬度//  [self getCoordinateByAddress:@"中国山西省朔州市右玉县新城镇绿源小区"];    // 编译和反编译不能同时走,设置不同的按钮来分别实现    // 根据经纬度反编译取出地名//  [self getAddressByLongitude:112.4750 Latitude:39.98];    // 计算两点之间的距离//  [self distanceForDeuce];}#pragma mark - 计算两点之间的距离- (void)distanceForDeuce{  // 创建位置  CLLocation *locationBeijing = [[CLLocation alloc] initWithLatitude:40 longitude:116];  CLLocation *locationYouyu = [[CLLocation alloc] initWithLatitude:39.98 longitude:112.47];    // 计算距离  CLLocationDistance distance = [locationBeijing distanceFromLocation:locationYouyu];    NSLog(@"%f公里", distance / 1000);}#pragma mark - 根据经纬度反编译取出地名- (void)getAddressByLongitude:(CLLocationDegrees)longitude           Latitude:(CLLocationDegrees)latitude{  // 反编码    // 创建CLLocation  CLLocation *location = [[CLLocation alloc] initWithLatitude:latitude longitude:longitude];    [self.geocoder reverseGeocodeLocation:location completionHandler:^(NSArray<CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error) {    NSDictionary *dict = placemarks.firstObject.addressDictionary;    NSLog(@"反编码地理位置信息:%@", dict);  }];}#pragma mark - 根据地名获取经纬度- (void)getCoordinateByAddress:(NSString *)address{  [self.geocoder geocodeAddressString:address completionHandler:^(NSArray<CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error) {    // 根据返回的地标,取出起一个位置(地标位置很多)    CLPlacemark *mark = placemarks.firstObject;        // 根据地标得到location    CLLocation *location = mark.location;    // 根据地标获取地区    CLRegion *region = mark.region;        // 获取字典信息    NSDictionary *addressDict = mark.addressDictionary;        NSLog(@"location = %@\n region = %@\n addressDict = %@", location, region, addressDict);        // 根据具体的需求进行选择输出//    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 - CLLocationManagerDelegate的代理方法// 定位成功后开始更新位置信息,只要移动的距离大于设置最小更新距离时也开始走这个方法- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations{  // 获取最后一次的位置  CLLocation *location = locations.lastObject;  // 获取位置坐标  CLLocationCoordinate2D coordinate = location.coordinate;  // 打印经纬度,longitude 经度 latitude 纬度  NSLog(@"经度:%f, 纬度:%f, 海拔:%f, 航向:%f, 行走速度:%f", coordinate.longitude, coordinate.latitude, location.altitude, location.course, location.speed);    // 为了节省电源,如果不使用定位,需要把定位关闭  [self.manager stopUpdatingLocation];}- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error{  NSLog(@"定位失败");}@end

View Code

三、地图显示(MapKit框架)

 1、MapKit框架的使用

  • 导入框架(iOS5之后不再需要程序员自己导入)

   蓝色发光圆点就是用户的当前位置,专业术语叫做 "大头针"

 3、地图的类型

  可以通过设置MKMapView的mapType设置地图类型(mapViewType是枚举类型)

MKMapTypeStandard // 普通地图(左图)MKMapTypeSatellite // 卫星云图 (中图)MKMapTypeHybrid // 普通地图覆盖于卫星云图之上(右图)MKMapTypeSatelliteFlyover NS_ENUM_AVAILABLE(10_11, 9_0) // 地形和建筑物的三维模型MKMapTypeHybridFlyover NS_ENUM_AVAILABLE(10_11, 9_0) // 显示道路和附加元素的Flyover

 4、MKMapView的代理

  • MKMapView可以设置一个代理对象 MKMapViewDelegate,用来监听地图的相关行为,常见的代理方法有:
// 一个位置更改默认只会调用一次,不断监测用户的当前位置时每次都会调用这个方法,把用户的最新位置(userLocation参数)传进来.- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation;// 地图的显示区域即将发生改变的时候调用- (void)mapView:(MKMapView *)mapView regionWillChangeAnimated:(BOOL)animated;// 地图的显示区域已经发生改变的时候调用- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated;

  • MKUserLocation其实是个大头针模型,包括以下属性:
// 显示在大头针上的标题@property (nonatomic, copy) NSString *title;// 显示在大头针上的子标题@property (nonatomic, copy) NSString *subtitle;// 地理位置信息(大头针钉在什么地方)@property (readonly, nonatomic) CLLocation *location;

 5、设置地图的显示

  • 通过MKMapView的下列方法,可以设置地图显示的位置和区域
// 设置地图的中心点位置@property (nonatomic) CLLocationCoordinate2D centerCoordinate;-(void)setCenterCoordinate: (CLLocationCoordinate2D)coordinate animated:(BOOL)animated;@property (nonatomic) MKCoordinateRegion region;- (void)setRegion:(MKCoordinateRegion)region animated:(BOOL)animated;

  • MKCoordinateRegion是一个用来表示区域的结构体,定义如下:
typedef struct {  CLLocationCoordinate2D center; // 区域的中心点位置   MKCoordinateSpan span; // 区域的跨度} MKCoordinateRegion;

  • MKCoordinateSpan的定义:
typedef struct {  CLLocationDegrees latitudeDelta; // 纬度跨度  CLLocationDegrees longitudeDelta; // 经度跨度} MKCoordinateSpan;

 6、大头针

  • 新建一个大头针模型类,继承 NSObject,遵循 MKAnnotation 协议就是一个大头针!
#import <Foundation/Foundation.h>#import <MapKit/MapKit.h>@interface MyAnnotation : NSObject <MKAnnotation>// 重写协议中的三个属性:coordinate(标记位置)、title(标题)、subtitle(子标题)/// 坐标@property (nonatomic) CLLocationCoordinate2D coordinate;/// 标题@property (nonatomic, copy) NSString *title;/// 子标题@property (nonatomic, copy) NSString *subtitle;@end

  • 大头针的基本操作
// 添加一个大头针- (void)addAnnotation:(id <MKAnnotation>)annotation;// 添加多个大头针- (void)addAnnotations:(NSArray *)annotations;// 移除一个大头针- (void)removeAnnotation:(id <MKAnnotation>)annotation;// 移除多个大头针- (void)removeAnnotations:(NSArray *)annotations;

 7、简单实例代码

#import "ViewController.h"// 地图使用的空间#import <MapKit/MapKit.h>#import <CoreLocation/CoreLocation.h>#import "MyAnnotation.h"@interface ViewController () <MKMapViewDelegate>/// 定位管理器@property (nonatomic, strong) CLLocationManager *locationManager;/// 显示地图的视图@property (nonatomic, strong) MKMapView *mapView;@end@implementation ViewController- (void)viewDidLoad {  [super viewDidLoad];  // Do any additional setup after loading the view, typically from a nib.    // 创建地图视图  [self createMapView];}#pragma mark - 创建地图视图- (void)createMapView{  // 创建地图,并添加到当前视图上  self.mapView = [[MKMapView alloc] initWithFrame:[UIScreen mainScreen].bounds];    [self.view addSubview:self.mapView];    // 设置代理  self.mapView.delegate = self;    // 定位  self.locationManager = [[CLLocationManager alloc] init];  if (![CLLocationManager locationServicesEnabled]) {    NSLog(@"当前设备定位不可用");  }  if ([CLLocationManager authorizationStatus] != kCLAuthorizationStatusAuthorizedWhenInUse) {    [self.locationManager requestWhenInUseAuthorization];  }    // 设置地图的定位追踪的模式  self.mapView.userTrackingMode = MKUserTrackingModeFollow;  // 设置地图的显示类型  self.mapView.mapType = MKMapTypeStandard;    // 添加大头针  [self addAnnotation];  }#pragma mark - 添加大头针- (void)addAnnotation{  // 设置位置北京  CLLocationCoordinate2D location1 = CLLocationCoordinate2DMake(40, 116);  MyAnnotation *annotation = [[MyAnnotation alloc] init];  annotation.coordinate = location1;  annotation.title = @"北京";  annotation.subtitle = @"一个古老的城市";    // 设置位置右玉  CLLocationCoordinate2D location2 = CLLocationCoordinate2DMake(39.98, 112.47);  MyAnnotation *annotation2 = [[MyAnnotation alloc] init];  annotation2.coordinate = location2;  annotation2.title = @"右玉";  annotation2.subtitle = @"生我养我的地方";    // 大连  CLLocationCoordinate2D location3 = CLLocationCoordinate2DMake(38.92, 121.62);  MyAnnotation *annotation3 = [[MyAnnotation alloc] init];  annotation3.coordinate = location3;  annotation3.title = @"大连";  annotation3.subtitle = @"一个喝啤酒吃蛤蜊的城市";    NSArray *annotationArray = @[annotation, annotation2, annotation3];    [self.mapView addAnnotations:annotationArray];}#pragma mark - 代理方法- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation{  NSLog(@"%@", userLocation);}@end

View Code

四、自定义大头针

  很多情况下,需要自定义大头针的显示样式,比如显示一张图片