码迷,mamicode.com
首页 > 移动开发 > 详细

iOS进阶学习-地图

时间:2016-05-31 23:46:37      阅读:289      评论:0      收藏:0      [点我收藏+]

标签:

一、地图的简介

  • 在移动互联网时代,移动app能解决用户的很多生活琐事,比如:
  • 导航:去任意陌生的地方。
  • 周边:找餐馆、找酒店、找银行、找电影院。
  • 手机软件:微信摇一摇、QQ附近的人、微博、支付宝等。
  • 在上述应用中,都用到了地图和定位功能,在iOS开发中,要想加入这两大功能,必须基于两个框架进行开发:
  • Map Kit :用于地图展示。
  • Core Location :用于地理定位。

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

  1、CoreLocation框架的使用

  • 导入头文件:#import <CoreLocation/CoreLocation.h>
  • 注意:
  • CoreLocation框架中所有数据类型的前缀都是CL
  • CoreLocation中使用CLLocationManager对象来做用户定位

  2、用户隐私的保护

  • 从iOS 7之后,苹果在保护用户隐私方面做了很大的加强,以下操作都必须经过用户批准授权:
  • ①要想获得用户的位置和访问用户的通讯录、日历、相机、相册等等都需要用户来手动授权。
  • ②当想访问用户的隐私信息时,系统会自动弹出一个对话框让用户授权。
  • 技术分享
  • 开发者可以在Info.plist中设置NSLocationUsageDescription说明定位的目的(Privacy - Location Usage Description),一旦用户选择了“Don’t Allow”,意味着你的应用以后就无法使用定位功能,为了严谨起见,最好在使用定位功能之前判断当前应用的定位功能是否可用。
  • CLLocationManager有个类方法可以判断当前应用的定位功能是否可用:+ (BOOL)locationServicesEnabled;
  • 技术分享
  • 代码示例:
// 引入库的头文件
#import <CoreLocation/CoreLocation.h>
// 遵守协议
@interface ViewController ()<CLLocationManagerDelegate>
/// 定位管理器
@property (nonatomic, strong) CLLocationManager *manager;

@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) {
        // 判断授权状态
        /*
         定位服务授权状态,返回枚举类型:
         kCLAuthorizationStatusNotDetermined: 用户尚未做出决定是否启用定位服务
         kCLAuthorizationStatusRestricted: 没有获得用户授权使用定位服务,可能用户没有自己禁止访问授权
         kCLAuthorizationStatusDenied :用户已经明确禁止应用使用定位服务或者当前系统定位服务处于关闭状态
         kCLAuthorizationStatusAuthorizedAlways: 应用获得授权可以一直使用定位服务,即使应用不在使用状态
         kCLAuthorizationStatusAuthorizedWhenInUse: 使用此应用过程中允许访问定位服务
         */
        if ([CLLocationManager authorizationStatus] != kCLAuthorizationStatusAuthorizedWhenInUse) {
            // 在授权请求之前需要在info.plist中设置允许定位的内容:NSLocationWhenInUseUsageDescription
            // 请求授权
            [self.manager requestWhenInUseAuthorization];
        }
    }
    // 第三步:设置管理器的代理
    self.manager.delegate = self;
    
    // 设置精确度CLLocationAccuracy 是一个枚举值
  /*
  最佳导航
    kCLLocationAccuracyBestForNavigation
  最精准
     kCLLocationAccuracyBest;
  10米
     kCLLocationAccuracyNearestTenMeters;
  百米
   kCLLocationAccuracyHundredMeters;
  千米
     kCLLocationAccuracyKilometer;
  3千米
     kCLLocationAccuracyThreeKilometers;     
  */
self.manager.desiredAccuracy = kCLLocationAccuracyHundredMeters; // 设置最小更新距离(单位:米) self.manager.distanceFilter = 10; // 开启定位 [self.manager startUpdatingLocation]; } #pragma mark - CLLocationManagerDelegate代理方法 /* 注意: 1.定位频率和定位精度并不应当越精确越好,需要视实际情况而定,因为越精确越耗性能,也就越费电。 2.定位成功后会根据设置情况频繁调用-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations方法,这个方法返回一组地理位置对象数组,每个元素一个CLLocation代表地理位置信息(包含经度、纬度、海报、行走速度等信息),之所以返回数组是因为有些时候一个位置点可能包含多个位置。 3.使用完定位服务后如果不需要实时监控应该立即关闭定位服务以节省资源。 4.除了提供定位功能,CLLocationManager还可以调用startMonitoringForRegion:方法对指定区域进行监控。 */ // 定位成功之后开始更新位置信息,只要移动设置的最小距离之后也开始走这个方法 - (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations { // 获取最后一次的位置(经纬度)
  //CLLocation用来表示某个位置的地理信息,比如经纬度、海拔等等

  // 经纬度
  @property(readonly, nonatomic) CLLocationCoordinate2D coordinate;

  // 海拔
  @property(readonly, nonatomic) CLLocationDistance altitude;

  // 路线,航向(取值范围是0.0° ~ 359.9°,0.0°代表正北方向)
  @property(readonly, nonatomic) CLLocationDirection course;
  
  // 行走速度(单位是m/s)
  @property(readonly, nonatomic) CLLocationSpeed speed;

  // 此方法可以计算2个位置(CLLocation)之间的距离
  - (CLLocationDistance)distanceFromLocation:(const CLLocation *)location
CLLocation *location = locations.lastObject; CLLocationCoordinate2D coordinate = location.coordinate; NSLog(@"**********latitude = %f**********, **********longitude = %f**********, **********altitude = %f**********, 航向 = %f, 行走速度 = %f", coordinate.latitude, coordinate.longitude, locations.lastObject.altitude, locations.lastObject.course, locations.lastObject.speed); // 为了节省电源,如果不适用定位,需要把定位关掉 [self.manager stopUpdatingLocation]; } // 定位失败 - (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error { NSLog(@"定位失败"); }

  3、地理编码与地理反编码

  • 使用CLGeocoder可以完成"地理编码"和"反地理编码"。
  • 地理编码:根据给定的地名,获得具体的位置信息(比如经纬度、地址的全称等)。
  • 反地理编码:根据给定的经纬度,获得具体的位置信息。
  • 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;
  • 代码示例:
@interface ViewController ()<CLLocationManagerDelegate>
/// CLGeocoder能进行地理位置的编码和反编码
@property (nonatomic, strong) CLGeocoder *geocoder;

@end
- (void)viewDidLoad {
    // 初始化对象
    self.geocoder = [[CLGeocoder alloc] init];
    // 根据地名获取经纬度
    [self getCoordinateByAddress:@"北京"];
    // 根据经纬度反编码取出地名
    [self getAddressByLongitude:116 Latitude:40];
    // 计算两个位置之间的距离
    [self distance];
}

#pragma mark - 根据地名获取相关的信息
- (void)getCoordinateByAddress:(NSString *)address
{
    // 编码方法
    [self.geocoder geocodeAddressString:address completionHandler:^(NSArray<CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error) {
        NSLog(@"count1111 = %ld", placemarks.count);
        for (int i = 0; i < placemarks.count; i++) {
            // 根据返回的地标,取出第一个位置(地标的位置很多)
            CLPlacemark *mark = placemarks[i];
            // 根据地标得到location
            CLLocation *location = mark.location;
            // 根据location获取区域
            CLRegion *region = mark.region;
            // 获取字典信息
            NSDictionary *addressDic = mark.addressDictionary;
            
            NSLog(@"地标 = %@,区域 = %@,字典 = %@",location,region,addressDic);
        }
        
    }];
}

#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.lastObject.addressDictionary;
        NSLog(@"count=  %ld", placemarks.count);
        
        NSLog(@"dic = %@", dict);
    }];
}
#pragma mark - 计算两个位置之间的距离
- (void)distance
{
    /*
     北京的经纬度是 北纬40,东经116
     大连的:北纬39,东经121
     */
    // 创建位置1
    CLLocation *locationBeiJing = [[CLLocation alloc] initWithLatitude:40 longitude:116];
    // 创建位置2
    CLLocation *locationDaLian = [[CLLocation alloc] initWithLatitude:39 longitude:121];
    CLLocationDistance distance = [locationBeiJing distanceFromLocation:locationDaLian];
    NSLog(@"北京到大连的距离:%f", distance);
    
    
    
}

三、地图显示(MapKit框架)

  1、MapKit框架的使用

  • 导入主头文件:#import <MapKit/MapKit.h>
  • MapKit框架使用须知:
  • MapKit框架中所有数据类型的前缀都是MK。
  • MapKit有一个比较重要的UI控件:MKMapView,专门用于地图显示。

  2、跟踪显示用户的位置

  • 设置MKMapView的userTrackingMode属性可以跟踪显示用户的当前位置  。 
  • MKUserTrackingModeNone :不跟踪用户的位置。
  • MKUserTrackingModeFollow :跟踪并在地图上显示用户的当前位置。
  • MKUserTrackingModeFollowWithHeading :跟踪并在地图上显示用户的当前位置,地图会跟随用户的前进方向进行旋转。
  • 下图是跟踪效果:
  • 技术分享
  • 蓝色发光圆点就是用户的当前位置。
  • 蓝色发光圆点,专业术语叫做“大头针”。

  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可以设置一个代理对象,用来监听地图的相关行为,常见的代理方法有:
// 一个位置更改默认只会调用一次,不断监测用户的当前位置时每次都会调用这个方法,把用户的最新位置(userLocation参数)传进来.

- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation;

// 地图的显示区域即将发生改变的时候调用

- (void)mapView:(MKMapView *)mapView regionWillChangeAnimated:(BOOL)animated;

// 地图的显示区域已经发生改变的时候调用

- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated;

  5、MKUserLocation

  • MKUserLocation其实是个大头针模型,包括以下属性:
// 显示在大头针上的标题
@property (nonatomic, copy) NSString *title;

// 显示在大头针上的子标题
@property (nonatomic, copy) NSString *subtitle;

// 地理位置信息(大头针钉在什么地方)
@property (readonly, nonatomic) CLLocation *location;

  6、设置地图的显示

  • 通过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;

  7、大头针

  • 大头针:在iOS开发中经常会标记某个位置,需要使用地图标注,也就是大家俗称的“大头针”。只要一个NSObject类实现MKAnnotation协议就可以作为一个大头针,通常会重写协议中coordinate(标记位置)、title(标题)、subtitle(子标题)三个属性,然后在程序中创建大头针对象并调用addAnnotation:方法添加大头针即可(之所以iOS没有定义一个基类实现这个协议供开发者使用,多数原因应该是MKAnnotation是一个模型对象,对于多数应用模型会稍有不同,例如后面的内容中会给大头针模型对象添加其他属性)。

  8、代码示例:

#import "ViewController.h"
//引入框架
#import <MapKit/MapKit.h>
#import <CoreLocation/CoreLocation.h>
#import "MyAnnotation.h"
@interface ViewController ()<MKMapViewDelegate>

/// 定位管理器
@property (nonatomic, strong) CLLocationManager *manager;
/// 显示地图的视图
@property (nonatomic, strong) MKMapView *mapView;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // 创建视图
    [self createMapView];
}
#pragma mark - 创建视图
- (void)createMapView
{
    // 创建地图并添加到当前视图上
    self.mapView = [[MKMapView alloc] initWithFrame:[UIScreen mainScreen].bounds];
    [self.view addSubview:self.mapView];
    // 设置代理
    self.mapView.delegate = self;
    
    // 定位
    self.manager = [[CLLocationManager alloc] init];
    if (![CLLocationManager locationServicesEnabled]) {
        NSLog(@"当前设备定位不可用");
    }
    if ([CLLocationManager authorizationStatus] != kCLAuthorizationStatusAuthorizedWhenInUse) {
        [self.manager requestWhenInUseAuthorization];
    }
    // 设置地图的定位追踪
    self.mapView.userTrackingMode = MKUserTrackingModeFollow;
    // 设置地图的显示类型
    self.mapView.mapType = MKMapTypeStandard;
    // 添加大头针
    [self addAnnotation];
}

#pragma mark - 添加大头针
- (void)addAnnotation
{
    // 设置位置
    CLLocationCoordinate2D loaction1 = CLLocationCoordinate2DMake(40, 116);
    
    MyAnnotation *annotation = [[MyAnnotation alloc] init];
    annotation.coordinate = loaction1;
    annotation.title = @"北京";
    annotation.subtitle = @"北京市";
    
    // 大连
    CLLocationCoordinate2D location2 = CLLocationCoordinate2DMake(39, 121);
    MyAnnotation *annotation2 = [[MyAnnotation alloc] init];
    annotation2.coordinate = location2;
    annotation2.title = @"大连";
    annotation2.subtitle = @"大连市";
    
    
    // 上海
    CLLocationCoordinate2D location3 = CLLocationCoordinate2DMake(31.23, 121.47);
    MyAnnotation *annotation3 = [[MyAnnotation alloc] init];
    annotation3.coordinate = location3;
    annotation3.title = @"上海";
    annotation3.subtitle = @"上海市";
    [self.mapView addAnnotations:@[annotation, annotation2, annotation3]];
}
- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end

  大头针模型:

@interface MyAnnotation : NSObject<MKAnnotation>
// 重写协议中的三个属性

// 通常会重写协议中coordinate(标记位置)、title(标题)、subtitle(子标题)三个属性
@property (nonatomic) CLLocationCoordinate2D coordinate;
@property (nonatomic, copy) NSString *title;
@property (nonatomic, copy) NSString *subtitle;


@end

四、自定义大头针

  • 很多情况下,需要自定义大头针的显示样式,比如显示一张图片:
  • 技术分享
  • 地图上的大头针控件是MKAnnotationView:
//MKAnnotationView的属性

// 大头针模型
@property (nonatomic, strong) id <MKAnnotation> annotation;

// 显示的图片
@property (nonatomic, strong) UIImage *image;

// 是否显示标注
@property (nonatomic) BOOL canShowCallout;

// 标注的偏移量
@property (nonatomic) CGPoint calloutOffset;

// 是否显示标注
@property (nonatomic) BOOL canShowCallout;

// 标注的偏移量
@property (nonatomic) CGPoint calloutOffset;

// MKPinAnnotationView是MKAnnotationView的子类

// MKPinAnnotationView比MKAnnotationView多了2个属性

// 大头针颜色
@property (nonatomic) MKPinAnnotationColor pinColor;

// 大头针第一次显示时是否从天而降
@property (nonatomic) BOOL animatesDrop;
  • 设置MKMapView的代理实现下面的代理方法,返回大头针控件
#pragma mark - 实现自定义大头针视图的代理方法
// 显示大头针的时候才会调用的
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation
{
    // 判断是否是当前自定义的大头针类
    if ([annotation isKindOfClass:[MyAnnotation class]]) {
        // 先定义一个重用标识
        static NSString *identifier = @"AnnotationOne";
        MKAnnotationView *annotationView = [mapView dequeueReusableAnnotationViewWithIdentifier:identifier];
        if (!annotationView) {
            annotationView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:identifier];
            // 允许用户交互
            annotationView.canShowCallout = YES;
            // 设置图片
            annotationView.image = [UIImage imageNamed:@"eye"];
            // 设置详情和大头针偏移量
            annotationView.calloutOffset = CGPointMake(0, 3);
            // 设置详情的左视图
            annotationView.leftCalloutAccessoryView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"Y0N[2Z@P[]SRYYA@I0CNNTO.jpg"]];
        }
        return annotationView;
    } else {
        return nil;
    }
}

iOS进阶学习-地图

标签:

原文地址:http://www.cnblogs.com/soley/p/5547718.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!