地图与定位

iOS开发中,要想加入地图定位功能,必须基于2个框架进行开发
MapKit :用于地图展示
CoreLocation :用于地理定位

2个热门专业术语
LBSLocationBasedService
SoLoMoSocialLocalMobile(索罗门)

CoreLocation框架的使用
导入主头文件

#import <CoreLocation/CoreLocation.h>

CoreLocation框架使用须知:
CoreLocation框架中所有数据类型的前缀都是CL
CoreLocation中使用CLLocationManager对象来做用户定位

CLLocationManager 的常用操作
开始用户定位
- ( void)startUpdatingLocation;
停止用户定位
- ( void)stopUpdatingLocation;

当调用了startUpdatingLocation 方法后,就开始不断地定位用户的位置,中途会频繁地调用代理的下面方法
- ( void)locationManager:( CLLocationManager*)manager didUpdateLocations:( NSArray*)locations;
locations 参数里面装着 CLLocation 对象

CLLocation
CLLocation 用来表示某个位置的地理信息,比如经纬度、海拔等等
@property( readonly, nonatomic) CLLocationCoordinate2D  coordinate;
经纬度

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

@property( readonly, nonatomic) CLLocationDirection  course;
路线,航向(取值范围是 0 . ~ 359.9° 0.0° 代表真北方向)

@property( readonly, nonatomic) CLLocationSpeed  speed;
行走速度(单位是 m/s

用 - ( CLLocationDistance)distanceFromLocation:( const CLLocation*)location 方法可以计算 2 个位置之间的距离

CLLocationManager
@property(assign,nonatomic)CLLocationDistance distanceFilter;
每隔多少米定位一次

@property(assign,nonatomic)CLLocationAccuracy desiredAccuracy;
定位精确度(越精确就越耗电)

CLLocationCoordinate2D

typedef struct {

        CLLocationDegreeslatitude;// 纬度

        CLLocationDegreeslongitude;// 经度

} CLLocationCoordinate2D;

一般用CLLocationCoordinate2DMake函数来创建CLLocationCoordinate2D

示例:
#import "ViewController.h"
#import <CoreLocation/CoreLocation.h>

@interface ViewController () <CLLocationManagerDelegate>

@property(nonatomic,strong)CLLocationManager *mgr;

@end

@implementation ViewController

#pragma mark - 懒加载
- (CLLocationManager *)mgr
{
    if (_mgr == nil) {
        // 1.创建定位管理者
        _mgr = [[CLLocationManager alloc] init];
        
        // 2.设置代理(我们得通过代理获取用户的位置信息)
        _mgr.delegate = self;
        
        // 3.位置间隔之后重新定位
        _mgr.distanceFilter = 10;
        
        // 4.定位的精确度
        _mgr.desiredAccuracy = kCLLocationAccuracyBestForNavigation;
    }
    return _mgr;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    
    /*
    // 1.创建定位管理者
    CLLocationManager *mgr = [[CLLocationManager alloc] init];
    
    // 2.设置代理
    mgr.delegate = self;
    */
    // 3.开始定位
    [self.mgr startUpdatingLocation];
    
    // 4.计算两个经纬度之间的距离
    [self countDistance];
}

/**
 *  计算两个经纬度之间的距离
 */
- (void)countDistance
{
    CLLocation *location1 = [[CLLocation alloc] initWithLatitude:23.23 longitude:113.33];
    CLLocation *location2 = [[CLLocation alloc] initWithLatitude:40.06 longitude:116.39];
    
    CLLocationDistance distance = [location1 distanceFromLocation:location2];
    NSLog(@"%f", distance);
}

/**
 *  定位到用户的位置会调用该方法(并且该方法调用非常频繁)
 *
 *  @param locations 存放着定位的所有位置
 */
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
    // 1.获取用户位置的对象
    CLLocation *location = [locations lastObject];
    CLLocationCoordinate2D coordinate = location.coordinate;
    NSLog(@"纬度:%f 经度:%f", coordinate.latitude, coordinate.longitude);
    
    // 2.停止定位
    [manager stopUpdatingLocation];
}

CLGeocoder
使用CLGeocoder可以完成“地理编码”和“反地理编码”
地理编码:根据给定的地名,获得具体的位置信息(比如经纬度、地址的全称等)
反地理编码:根据给定的经纬度,获得具体的位置信息

地理编码方法
- (void)geocodeAddressString:(NSString *)addressString completionHandler:(CLGeocodeCompletionHandler)completionHandler;

反地理编码方法
- (void)reverseGeocodeLocation:(CLLocation*)location completionHandler:(CLGeocodeCompletionHandler)completionHandler;

当地理\反地理编码完成时,就会调用CLGeocodeCompletionHandler
typedef void (^CLGeocodeCompletionHandler)(NSArray *placemarks,NSError*error);
这个block传递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;
城市

补充.


1.CLPlacemarkaddressDictionary属性 遍历字典数据


addressDictionary enumerateKeysAndObjectsUsingBlock


2.block是在主线程调用,所以可以直接在block刷新UI


name                    :   地名

thoroughfare            :   街道

ubThoroughfare          :   街道相关信息,例如门牌等

locality                :   城市

subLocality             :   城市相关信息,例如标志性建筑

administrativeArea      :   直辖市

subAdministrativeArea   :   其他行政区域信息

postalCode              :   邮编

ISOcountryCode          :   国家编码

country;                :   国家

inlandWater             :   水源、湖泊

ocean;                  :   海洋

areasOfInterest         :   关联的或利益相关的地标




地理编码:
// 地理编码
    CLGeocoder *geocoder = [[CLGeocoder alloc] init];
    [geocoder geocodeAddressString:address completionHandler:^(NSArray<CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error) {
        // 如果解析有错误,或者解析出的数组个数为0,直接返回
        if (placemarks.count == 0 || error) return ;
        
        // 遍历所有的地标对象
        for (CLPlacemark *pm in placemarks) {
            // 取出用户的位置信息
            CLLocation *location = pm.location;
            // 取出用户的经纬度
            CLLocationCoordinate2D coordinate = location.coordinate;
            
            // 将信息设置到界面上
            self.latitude.text = [NSString stringWithFormat:@"%.2f", coordinate.latitude];
            self.longitude.text = [NSString stringWithFormat:@"%.2f", coordinate.longitude];
            self.result.text = pm.name;
        }
    }];

反地理编码:
// 2.反地理编码
    CLGeocoder *geocoder = [[CLGeocoder alloc] init];
    CLLocation *location = [[CLLocation alloc] initWithLatitude:latitude.floatValue longitude:longitude.floatValue];
    [geocoder reverseGeocodeLocation:location completionHandler:^(NSArray *placemarks, NSError *error) {
        // 如果有错误,或者解析出来的地址数量为0
        if (placemarks.count == 0 || error) return ;
        
        // 取出地标,就可以取出地址信息,以及CLLocation对象
        CLPlacemark *pm = [placemarks firstObject];
        
#warning 注意:如果是取出城市的话,需要判断locality属性是否有值(直辖市时,该属性为空)
        if (pm.locality) {
            self.resultLabel.text = pm.locality;
        } else {
            self.resultLabel.text = pm.administrativeArea;
        }
    }];

MapKit 框架的使用
MapKit框架使用时需要导包
导入主头文件

#import <MapKit/MapKit.h>

MapKit框架使用须知
MapKit框架中所有数据类型的前缀都是MK
MapKit有一个比较重要的UI控件MKMapView,专门用于地图显示

跟踪显示用户的位置
设置 MKMapView userTrackingMode 属性可以跟踪显示用户的当前位置
MKUserTrackingModeNone :不跟踪用户的位置
MKUserTrackingModeFollow 跟踪并在地图上显示用户的当前位置
MKUserTrackingModeFollowWithHeading 跟踪并在地图上显示用户的当前位置,地图会跟随用户的前进方向进行旋转

地图的类型
可以通过设置 MKMapView mapViewType 设置地图类型
MKMapTypeStandard 普通地图(左图)
MKMapTypeSatellite 卫星云图 (中图)
MKMapTypeHybrid 普通地图覆盖于卫星云图之上(右图)
                  
MKMapView的代理
MKMapView 可以设置一个代理对象,用来监听地图的相关行为

常见的代理方法有
- ( void)mapView:( MKMapView *)mapView didUpdateUserLocation:( MKUserLocation *)userLocation;
一个位置更改默认只会调用一次, 不断监测用户的当前位置
每次调用,都会把用户的最新位置( userLocation 参数)传进来

- ( void)mapView:( MKMapView *)mapView regionWillChangeAnimated:( BOOL)animated;
地图的显示区域即将发生改变的时候调用

- ( void)mapView:( MKMapView *)mapView regionDidChangeAnimated:( BOOL)animated;
地图的显示区域已经发生改变的时候调用

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

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

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

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

设置地图的显示区域
@property ( nonatomic) MKCoordinateRegion  region;
- ( void)setRegion:( MKCoordinateRegion)regionanimated:( BOOL)animated;
MKCoordinateRegion
MKCoordinateRegion 是一个用来表示区域的结构体,定义如下

typedef struct {

        CLLocationCoordinate2D center; //区域的中心点位置

        MKCoordinateSpan span;//区域的跨度

} MKCoordinateRegion;

MKCoordinateSpan 的定义

typedef struct {

   CLLocationDegrees latitudeDelta; //纬度跨度

   CLLocationDegrees longitudeDelta; //经度跨度

} MKCoordinateSpan;







ios7地图的基本使用
// 显示地图的View
@property (weak, nonatomic) IBOutlet MKMapView *mapView;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 设置代理
    self.mapView.delegate = self;
    
    // 跟踪用户的位置
    self.mapView.userTrackingMode = MKUserTrackingModeFollow;
    
    // 设置地图类型
    self.mapView.mapType = MKMapTypeSatellite;
}

/**
 *  定位到用户的位置会执行该方法
 *
 *  @param userLocation 大头针模型
 */
- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation
{
    // 取出用户的位置
    CLLocationCoordinate2D coordinate = userLocation.location.coordinate;
    NSLog(@"纬度:%f 经度:%f", coordinate.latitude, coordinate.longitude);
    
    // 改变大头针显示的内容(通过改变大头针模型的属性)
    // userLocation.title = @"广州市";
    // userLocation.subtitle = @"广东省广州市天河区";
    CLGeocoder *geocoder = [[CLGeocoder alloc] init];
    CLLocation *location = userLocation.location;
    [geocoder reverseGeocodeLocation:location completionHandler:^(NSArray *placemarks, NSError *error) {
        if (placemarks.count == 0 || error) return;
        
        CLPlacemark *pm = [placemarks firstObject];
        
        if (pm.locality) {
            userLocation.title = pm.locality;
        } else {
            userLocation.title = pm.administrativeArea;
        }
        userLocation.subtitle = pm.name;
    }];
}

地图的显示区域:
#import "ViewController.h"
#import <MapKit/MapKit.h>

#define kLatitudeDelta 0.002703
#define kLongitudeDelta 0.001717

@interface ViewController () <MKMapViewDelegate>

// 显示地图的View
@property (weak, nonatomic) IBOutlet MKMapView *mapView;

/**
 * 点击之后回到用户的位置
 */
- (IBAction)backToUserLocation;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 设置代理
    self.mapView.delegate = self;
    
    // 跟踪用户的位置
    self.mapView.userTrackingMode = MKUserTrackingModeFollow;
    
    // 设置地图类型
    // self.mapView.mapType = MKMapTypeSatellite;
}

/**
 *  定位到用户的位置会执行该方法
 *
 *  @param userLocation 大头针模型
 */
- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation
{
    // 取出用户的位置
    CLLocationCoordinate2D coordinate = userLocation.location.coordinate;
    NSLog(@"纬度:%f 经度:%f", coordinate.latitude, coordinate.longitude);
    
    // 设置mapView显示的中心位置
    // [mapView setCenterCoordinate:coordinate animated:YES];
    // 设置mapView的显示区域
    MKCoordinateSpan span = MKCoordinateSpanMake(kLatitudeDelta, kLongitudeDelta);
    MKCoordinateRegion region = MKCoordinateRegionMake(coordinate, span);
    [mapView setRegion:region animated:YES];
}

- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated
{
    MKCoordinateRegion region = mapView.region;
    CLLocationCoordinate2D center = region.center;
    MKCoordinateSpan span = region.span;
    NSLog(@"纬度:%f 经度:%f", center.latitude, center.longitude);
    NSLog(@"纬度跨度:%f 经度跨度:%f", span.latitudeDelta, span.longitudeDelta);
}

// 点击按钮回到用户当前的位置
- (IBAction)backToUserLocation {
    // 地图的跨度
    MKCoordinateSpan span = MKCoordinateSpanMake(kLatitudeDelta, kLongitudeDelta);
    // 地图的显示区域
    MKCoordinateRegion region = MKCoordinateRegionMake(self.mapView.userLocation.location.coordinate, span);
    
    // 设置地图中心位置为用户的当前位置
    // [self.mapView setCenterCoordinate:self.mapView.userLocation.location.coordinate animated:YES];
    // 设置地图的显示区域
    [self.mapView setRegion:region animated:YES];
}

iOS8需要请求授权方式
需要在info.plist文件中添加NSLocationAlwaysUsageDescription字段


// 2.通过判断是否有该方法来判断是否需要请求requestAlwaysAuthorization授权
    if ([self.mgr respondsToSelector:@selector(requestAlwaysAuthorization)]) {
        [self.mgr requestAlwaysAuthorization];
    }


大头针的基本操作
添加一个大头针
- ( void)addAnnotation:( id < MKAnnotation>)annotation;
添加多个大头针
- ( void)addAnnotations:( NSArray*)annotations;
移除一个大头针
- ( void)removeAnnotation:( id < MKAnnotation>)annotation;
移除多个大头针
- ( void)removeAnnotations:( NSArray*)annotations;
( id < MKAnnotation>)annotation 参数是什么东西?
大头针模型对象:用来封装大头针的数据,比如大头针的位置、标题、子标题等数据

大头针模型
新建一个大头针模型类

#import <MapKit/MapKit.h>

@interface MyAnnotation : NSObject <MKAnnotation>

/**坐标位置*/

@property (nonatomic, assign) CLLocationCoordinate2D coordinate;

/**标题*/

@property (nonatomic, copy) NSString *title;

/**子标题*/

@property (nonatomic, copy) NSString *subtitle;

@end


MKAnnotationView
地图上的大头针控件是 MKAnnotationView

MKAnnotationView 的属性
@property ( nonatomic, strong) id < MKAnnotation> annotation;
大头针模型

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

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

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

@property ( strong, nonatomic) UIView  *rightCalloutAccessoryView;
标注右边显示什么控件

@property ( strong, nonatomic) UIView  *leftCalloutAccessoryView;
标注左边显示什么控件

MKPinAnnotationView
MKPinAnnotationView MKAnnotationView 的子类

MKPinAnnotationView MKAnnotationView 多了 2 个属性
@property ( nonatomic) MKPinAnnotationColor  pinColor;
大头针颜色

@property ( nonatomic) BOOL  animatesDrop;
大头针第一次显示时是否从天而降

添加大头针
- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 1.设置代理
    self.mapView.delegate = self;
    
    // 2.跟踪用户的位置
    self.mapView.userTrackingMode = MKUserTrackingModeFollow;
    
    // 3.添加两个大头针
    MyAnnotation *anno1 = [[MyAnnotation alloc] init];
    anno1.coordinate = CLLocationCoordinate2DMake(40.06, 116.39);
    anno1.title = @"北京市";
    anno1.subtitle = @"中国北京市昌平区";
    
    MyAnnotation *anno2 = [[MyAnnotation alloc] init];
    anno2.coordinate = CLLocationCoordinate2DMake(30.23, 120.23);
    anno2.title = @"杭州市";
    anno2.subtitle = @"浙江省杭州市萧山区";
    
    [self.mapView addAnnotation:anno1];
    [self.mapView addAnnotation:anno2];
}

/**
 *  定位到用户的位置会调用该方法
 *
 *  @param userLocation 大头针模型对象
 */
- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation
{
    // 设置用户的位置为地图的中心点
    [mapView setCenterCoordinate:userLocation.location.coordinate animated:YES];
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    // 1.获取用户点击的点
    CGPoint point = [[touches anyObject] locationInView:self.view];
    
    // 2.将该点转化成经纬度(地图上的坐标)
    CLLocationCoordinate2D coordinate = [self.mapView convertPoint:point toCoordinateFromView:self.view];
    
    // 3.添加大头针
    MyAnnotation *anno = [[MyAnnotation alloc] init];
    anno.coordinate = coordinate;
    anno.title = @"我的位置";
    anno.subtitle = @"你猜我在那?";
    [self.mapView addAnnotation:anno];
}

自定义大头针
/**
 *  在地图上添加一个大头针就会执行该方法
 *
 *  @param annotation 大头针模型对象
 *
 *  @return 大头针的View(返回nil表示默认使用系统, 默认MKAnnotationView是不可见)
 */
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation
{
    // 1.如果是用户位置的大头针,直接返回nil,使用系统的
    if ([annotation isKindOfClass:[MKUserLocation class]]) return nil;
    
    // 2.创建标识
    static NSString *ID = @"annoView";
    // 3.从缓冲池中取出大头针的View
    MKPinAnnotationView *annoView = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:ID];
    // 4.如果为nil,则创建
    if (annoView == nil) {
        annoView = [[MKPinAnnotationView alloc] initWithAnnotation:nil reuseIdentifier:ID];
        
        // 1.设置标题和子标题可以呼出
        annoView.canShowCallout = YES;
        
        // 2.设置大头针的颜色
        annoView.pinColor = MKPinAnnotationColorPurple;
        
        // 3.掉落效果
        annoView.animatesDrop = YES;
    }
    
    // 5.设置大头针的大头针模型
    annoView.annotation = annotation;
    
    return annoView;
}

使用苹果内置地图做导航
#import "ViewController.h"
#import <MapKit/MapKit.h>

@interface ViewController ()

// 目的地的输入框
@property (weak, nonatomic) IBOutlet UITextField *destinationField;

/**
 *  点击按钮之后开始导航
 */
- (IBAction)navigate;

@end

@implementation ViewController

- (IBAction)navigate {
    // 1.拿到用户输入的目的地
    NSString *destination = self.destinationField.text;
    if (destination.length == 0) {
        return;
    }
    
    // 2.地理编码
    CLGeocoder *geocoder = [[CLGeocoder alloc] init];
    [geocoder geocodeAddressString:destination completionHandler:^(NSArray *placemarks, NSError *error) {
        if (placemarks.count == 0 || error) return ;
        
        // 2.1.取出地理编码出的地标
        CLPlacemark *clpm = [placemarks firstObject];
        
        // 2.2.利用CLPlacemark来创建MKPlacemark
        MKPlacemark *mkpm = [[MKPlacemark alloc] initWithPlacemark:clpm];
        
        // 2.3.利用MKPlacemark来创建目的地的MKMapItem
        MKMapItem *destinationItem = [[MKMapItem alloc] initWithPlacemark:mkpm];
        
        // 2.4.拿到起点的MKMapItem
        MKMapItem *sourceItem = [MKMapItem mapItemForCurrentLocation];
        
        // 2.5.开始导航
        [self startNavigateWithSourceItem:sourceItem destinationItem:destinationItem];
    }];
}

/**
 *  开始导航
 *
 *  @param sourceItem      起点的Item
 *  @param destinationItem 终点的Item
 */
- (void)startNavigateWithSourceItem:(MKMapItem *)sourceItem destinationItem:(MKMapItem *)destinationItem
{
    // 1.将起点和终点item放入数组中
    NSArray *items = @[sourceItem, destinationItem];
    
    // 2.设置Options参数(字典)
    NSDictionary *options = @{
            MKLaunchOptionsDirectionsModeKey : MKLaunchOptionsDirectionsModeDriving,
            MKLaunchOptionsMapTypeKey : @(MKMapTypeHybrid),
            MKLaunchOptionsShowsTrafficKey : @YES
                    };
    
    // 3.开始导航
    [MKMapItem openMapsWithItems:items launchOptions:options];
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值