在iOS的应用中,有很多的应用都是使用了核心定位功能和地图的相关功能!
在实际开发中,也有很多相关的针对地图和定位来做的第三方插件!比如:百度地图。
关于第三方地图和定位插件,也是利用iOS中的相关API做的扩展和整合。具体,使用第三方的插件,就需要了解它们的API。
今天了解的就是iOS原始的核心定位和地图API!
什么是核心定位和地图
CoreLocation
以及
Map
框架包通常能给我们的应用程序添加定位和地图相关的服务。
CoreLocation 框架包通常是使用硬件设备来进行定位服务的,Map框架包通常能够使你的应用程序做一些地图展示与交互的相关功能。
iOS将这两个功能封装到了两个库中。当然,我们需要在项目中引入该库,才能享受相应的服务。
这两个库分别是:
核心定位:
#import
一、添加一个简单的地图视图
*.h文件
#import
#import
#import
@interface MoreViewController :UIViewController<</span>MKMapViewDelegate,CLLocationManagerDelegate>{
}
@property (nonatomic,strong) MKMapView *myMapView;
@end
*.m文件
-(void)viewDidLoad
{
[superviewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
// 初始化MKMapView
self.myMapView =[[MKMapView alloc]initWithFrame:self.view.bounds];
self.myMapView.mapType = MKMapTypeHybrid;
[self.myMapView setDelegate:self];
self.myMapView.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight;
[self.view addSubview:self.myMapView];
//返回用户是否开启了设备上的“定位服务”功能
if([CLLocationManagerlocationServicesEnabled]){
self.myLocationManager = [[CLLocationManager alloc] init];
[self.myLocationManager setDelegate:self];
[self.myLocationManager setPurpose:@"一些提示信息,告诉用户要开启定位服务功能"];
// 开始更新地理位置,并根据更新结果执行CLLocationManagerDelegate方法
[self.myLocationManagerstartUpdatingLocation];
} else {
NSLog(@"设备上的“定位服务”功能未开启!");
}
}
说明:
在VC中,我们实现了两个协议,来捕捉相应的事件:
MKMapViewDelegate:关于地图的相关协议,定义了地图相关的协议方法。
CLLocationManagerDelegate
:关于核心定位的相关协议。
这两个协议有很多方法,慢慢来体会吧。
二、为地图定义锚点
为了更好的封装性,我们自定义了一个锚点的自定义类,来控制锚点。
忽然有点迷茫,为什么要自定义锚点类呢?
@interface MyAnnotation : NSObject <</span>MKAnnotation>
因为
MKAnnotation
协议中的属性都是只读的!我们要方便的设置,就必须要覆盖
MKAnnotation
协议中的属性。
*.h文件
#import
#import
@interface MyAnnotation :NSObject<<span style="color:#743fa4">MKAnnotation>{
}
//特别要注意这个参数需要标示为只读类型的。因为 MKAnnotation这个协议中定义的 Coordinate也是只读类型的。
@property (nonatomic,readonly) CLLocationCoordinate2D coordinate;
@property (nonatomic,copy, readonly) NSString *title;
@property (nonatomic,copy, readonly) NSString *subtitle;
@property (nonatomic, unsafe_unretained) MKPinAnnotationColor pinColor;// 气泡的颜色
// 初始化坐标(Coordinate)
-(id)initWithCoordinates:(CLLocationCoordinate2D)paramCoordinatestitle:(NSString *)paramTitlesubTitle:(NSString*)paramSubTitle;
@end
*.m文件
#import "MyAnnotation.h"
@implementation MyAnnotation
@synthesize coordinate;
@synthesize title;
@synthesize subtitle;
@synthesize pinColor;
-(id)initWithCoordinates:(CLLocationCoordinate2D)paramCoordinatestitle:(NSString *)paramTitlesubTitle:(NSString*)paramSubTitle{
self = [super init];
if (self != nil){
coordinate =paramCoordinates;
title =paramTitle;
subtitle =paramSubTitle;
pinColor = MKPinAnnotationColorGreen;//定义默认颜色
}
return(self);
}
@end
这样,我们就能够调用我们自定义的锚点类来进行锚点的添加!
贴:
#pragma mark - CLLocationManagerDelegate 定位服务
//当获得了新的位置时,调用该方法
-(void)locationManager:(CLLocationManager*)manager didUpdateToLocation:(CLLocation *)newLocationfromLocation:(CLLocation *)oldLocation{
NSLog(@"Latitude = %f", newLocation.coordinate.latitude);
NSLog(@"Longitude = %f",newLocation.coordinate.longitude);
// 定义一个2D坐标
CLLocationCoordinate2D location=CLLocationCoordinate2DMake(newLocation.coordinate.latitude,newLocation.coordinate.longitude);
//初始化锚点,根据坐标,标题,副标题
MyAnnotation *annotation =[[MyAnnotation alloc]initWithCoordinates:location
title:@"My Title"
subTitle:@"My Sub Title"];
// 为地图增加锚点
[self.myMapViewaddAnnotation:annotation];
}
//当无法获得位置时,调用该方法
-(void)locationManager:(CLLocationManager*)manager didFailWithError:(NSError *)error{
NSLog(@"无法获取经纬度!");
}
#pragma mark - MKMapViewDelegate
//当地图界面将要加载的时候会调用该方法
-(void)mapViewWillStartLoadingMap:(MKMapView*)mapView{
NSLog(@"mapViewWillStartLoadingMap");
}
三、为地图定义不同颜色的锚点
原来,我也觉的挺复杂的。或者是什么其他的特殊方式。
但是,实际用了,感觉原理跟TableView中的返回Cell的代理方法:
-(UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
是一个尿性的!对,原理一样啊!
也就是实现
MKMapViewDelegate协议的
mapView方法:
-(
MKAnnotationView
*)mapView:(
MKMapView
*)mapViewviewForAnnotation:(
id
)annotation
多的所以也不解释了!
贴:
//返回锚点(大头针)的View,根据坐标信息
-(MKAnnotationView*)mapView:(MKMapView *)mapViewviewForAnnotation:(id)annotation{
MKAnnotationView *result =nil;
if ([annotation isKindOfClass:[MyAnnotation class]] ==NO){
returnresult;
}
if ([mapView isEqual:self.myMapView] == NO){
returnresult;
}
MyAnnotation *senderAnnotation = (MyAnnotation*)annotation;
//获得锚点视图的颜色标识符
// 重用MKPinAnnotationView(跟重用TableView的Cell一个道理)
NSString *pinReusableIdentifier = @"myAnnotation";
MKPinAnnotationView*annotationView =(MKPinAnnotationView *) [mapViewdequeueReusableAnnotationViewWithIdentifier:pinReusableIdentifier];
if (annotationView == nil){
annotationView = [[MKPinAnnotationView alloc]initWithAnnotation:senderAnnotationreuseIdentifier:pinReusableIdentifier];
[annotationView setCanShowCallout:YES];
}
annotationView.pinColor =senderAnnotation.pinColor;
result = annotationView;
return result;
}
四、通过地理坐标(经纬度)得到地址信息
当我们得到了一组经纬度数据,并且想把这组数据解析出来得到一个实在的地理位置名称。
通过一组经纬度数据得到一个实在的地理位置数据,我们通常称之为逆向地理编码。
我们使用CLGeocoder类可以来完成这项任务!
贴:
#pragma mark - CLLocationManagerDelegate 定位服务
//当获得了新的位置时,调用该方法
-(void)locationManager:(CLLocationManager *)managerdidUpdateToLocation:(CLLocation*)newLocation fromLocation:(CLLocation *)oldLocation{
NSLog(@"Latitude = %f", newLocation.coordinate.latitude);
NSLog(@"Longitude = %f", newLocation.coordinate.longitude);
// 创建一个定位对象
CLLocation *thelocation = [[CLLocation alloc] initWithLatitude:newLocation.coordinate.latitude
longitude:newLocation.coordinate.longitude];
//初始化一个反向地理编码对象
self.myGeocoder = [[CLGeocoder alloc] init];
//根据给定的经纬度来得到相应的地址信息
[self.myGeocoder reverseGeocodeLocation:thelocationcompletionHandler:^(NSArray*placemarks, NSError *error){
if (error ==nil&& [placemarks count] > 0){
// CLPlacemark 存储着相应的地址数据
CLPlacemark *placemark =[placemarks objectAtIndex:0];
NSLog(@"Country = %@", placemark.country);
NSLog(@"Postal Code = %@", placemark.postalCode);
NSLog(@"Locality = %@",placemark.locality);
}
elseif (error == nil &&[placemarks count] ==0){
NSLog(@"No results werereturned.");
}
elseif (error != nil){
NSLog(@"An error occurred =%@", error); }
}];
}
有几点需要说明mark一下:
CLLocation:标识一个物理坐标对象。可以存储经纬度、海拔等信息。
CLGeocoder:这是一个提供物理地址(经纬度)到真实地址的转换的服务。换算出来的信息一般包括:国家、城市、州、街道等信息。而且,这个类需要基于联网状态下才能工作。因为数据都存储在苹果的数据库中。你懂的。
-(void)reverseGeocodeLocation:(CLLocation *)locationcompletionHandler:(CLGeocodeCompletionHandler)completionHandler方法:
用来转换地理坐标到真实地址的方法。
location:要转换的地址坐标
completionHandler:一个Block,请求返回时调用
在看一下
CLGeocodeCompletionHandler这个Block的方法签名。
typedef void(^CLGeocodeCompletionHandler)(NSArray *placemarks, NSError *error);
placemarks:返回查询的地理信息
error:是否成功
五、通过地址信息得到地理坐标(经纬度)
当然,我们应该想到,地理坐标与地址信息之间应该能够互相转化。
反向地理编码是通过一组经纬度数据的到一个实在的地理位置名称。同样我们可以使用地理编码通过一个地理名称得到一组经纬度数据。
CLGeocoder类可以来完成这项任务!
NSString *oreillyAddress =@"1005 Gravenstein HighwayNorth, Sebastopol, CA 95472, USA";
self.myGeocoder = [[CLGeocoder alloc] init];
[self.myGeocoder geocodeAddressString:oreillyAddresscompletionHandler:^(NSArray*placemarks, NSError *error){
if ([placemarks count] > 0 && error== nil){
NSLog(@"Found %lu placemark(s).",(unsigned long)[placemarks count]);
CLPlacemark *firstPlacemark = [placemarksobjectAtIndex:0];
NSLog(@"Longitude = %f",firstPlacemark.location.coordinate.longitude);
NSLog(@"Latitude = %f",firstPlacemark.location.coordinate.latitude);
}
else if ([placemarks count] == 0 &&
error == nil){
NSLog(@"Found no placemarks.");
}
else if (error != nil){
NSLog(@"An error occurred = %@", error);
}
}];