iOS·采用第三方(百度地图SDK)实现定位等功能开发
1.申请密钥
首先,申请一个baidu账号,接着进入新建密钥入口申请成为baidu地图开发者,填写相关开发者信息和短信验证码。接下来点击创建应用,如官方文档新建密钥文档指南里面的截图所示,注意正确勾选有关项。如果你想要实名认证,点击页面右上角的实名状态,下面是该页面的截图。
Paste_Image.png- 创建应用时,注意勾选正确的应用类型
默认是服务端类型,如果没有勾选iOS SDK类型,就不能根据你自己工程BundleID(即百度地图所谓的安全码)设置Key的选项了。如图所示,创建应用没有勾选iOS SDK,当点击设置,进去后根本没有定制化的设置选项。
Paste_Image.png- 创建应用时,注意正确填写iOS SDK安全码
在Xcode里面,找到自己工程的安全码,即工程的Bundle Identifier,如下图所示,应该是类似com.baidu.mapdemo等格式的字符串。
Paste_Image.png2.工程配置
2.1 CocoaPods方法
这种方法,优点是简单,不需要再对工程进行额外的配置。缺点是,没有自己根据需要选择性的加载百度开发包的余地,把整个SDK都导进来了,不管你有没有可能会用到这些包。
打开终端,输入类似下面的命令,cd到你自己工程的目录下
$ cd /Users/ChenMan/iOSLAB/myMapDemo/
1.创建Podfile:
touch Podfile
2.编辑Podfile内容如下:
pod 'BaiduMapKit' #百度地图SDK
3.在Podfile所在的文件夹下输入命令:
pod install (这个可能比较慢,请耐心等待……)
成功以后,会出现如下记录:
Analyzing dependencies
Downloading dependencies
Installing BaiduMapKit (2.9.1)
Generating Pods project
Integrating client project
[!] Please close any current Xcode sessions and use `***.xcworkspace` for this project from now on.
Sending stats
恭喜你已成功导入百度地图iOS SDK,现在就可以打开xcworkspace文件,在你的项目中使用百度地图SDK了
2.2 手动拷贝依赖库方法
这种方法的优点是,可以选择性的导入所需开发包,尽可能减小APP工程体积。缺点是步骤相对繁琐,总的来说分两部分工作,一是,选择性拷贝所需开发包到工程目录下并建立引用关系(手动拖拽,并勾选copy if needed,保证所需包被复制到工程目录下,而不是仅仅是引用关系),二是,在工程的TARGETS->Build Phases-> Link Binary With Libaries里面,添加前面添加的开发包。
接下来引用百度地图的文档说明,并作了适当改编:
- 第一步、根据需要导入 .framework包
百度地图 iOS SDK 采用分包的形式提供 .framework包,请广大开发者使用时确保各分包的版本保持一致。其中BaiduMapAPI_Base.framework为基础包,使用SDK任何功能都需导入,其他分包可按需导入。
全部分包和自定义分包的下载地址为 http://lbsyun.baidu.com/index.php?title=iossdk/sdkiosdev-download,如下图所示:
这里我选择下载的是自定义分包,我只需要单纯的定位功能注: 静态库中采用Objective-C++实现,因此需要您保证您工程中至少有一个.mm后缀的源文件(您可以将任意一个.m后缀的文件改名为.mm,比如AppDelegate.mm),或者在工程属性中指定编译方式,即在Xcode的Project -> Edit Active Target -> Build Setting 中找到 Compile Sources As,并将其设置为"Objective-C++"
- 第二步、引入所需的系统库
百度地图SDK中提供了定位功能和动画效果,v2.0.0版本开始使用OpenGL渲染,因此您需要在您的Xcode工程中引入CoreLocation.framework和QuartzCore.framework、OpenGLES.framework、SystemConfiguration.framework、CoreGraphics.framework、Security.framework、libsqlite3.0.tbd(xcode7以前为 libsqlite3.0.dylib)、CoreTelephony.framework 、libstdc++.6.0.9.tbd(xcode7以前为libstdc++.6.0.9.dylib)。
(注:粗体标识的系统库为v2.9.0新增的系统库,使用v2.9.0及以上版本的地图SDK,务必增加导入这3个系统库。)
添加方式: 在Xcode的Project -> Active Target ->Build Phases ->Link Binary With Libraries,添加这几个系统库即可。
- 第三步、引入所需的第三方openssl库:
添加支持HTTPS所需的penssl静态库:libssl.a和libcrypto.a(SDK打好的包存放于thirdlib目录下)
例如,我需要的是单纯的定位功能,选择自定义分包下载后,会看到如下图文件目录结构,静态库:libssl.a和libcrypto.a就在thirdlibs文件夹下面。
thirdlibs文件夹添加方法: 在 TARGETS->Build Phases-> Link Binary With Libaries中点击“+”按钮,在弹出的窗口中点击“Add Other”按钮,选择libssl.a和libcrypto.a添加到工程中
- 第四步、环境配置
在TARGETS->Build Settings->Other Linker Flags 中添加-ObjC。
- 第五步、引入mapapi.bundle资源文件
如果使用了基础地图功能,需要添加该资源,否则地图不能正常显示mapapi.bundle中存储了定位、默认大头针标注View及路线关键点的资源图片,还存储了矢量地图绘制必需的资源文件。如果您不需要使用内置的图片显示功能,则可以删除bundle文件中的image文件夹。您也可以根据具体需求任意替换或删除该bundle中image文件夹的图片文件。
方法:选中工程名,在右键菜单中选择Add Files to “工程名”…,从BaiduMapAPI_Map.framework||Resources文件中选择mapapi.bundle文件,并勾选“Copy items if needed”复选框,单击“Add”按钮,将资源文件添加到工程中。
- 第六步、引入头文件
在使用SDK的类 按需 引入下边的头文件:
#import <BaiduMapAPI_Base/BMKBaseComponent.h>//引入base相关所有的头文件
#import <BaiduMapAPI_Map/BMKMapComponent.h>//引入地图功能所有的头文件
#import <BaiduMapAPI_Search/BMKSearchComponent.h>//引入检索功能所有的头文件
#import <BaiduMapAPI_Cloud/BMKCloudSearchComponent.h>//引入云检索功能所有的头文件
#import <BaiduMapAPI_Location/BMKLocationComponent.h>//引入定位功能所有的头文件
#import <BaiduMapAPI_Utils/BMKUtilsComponent.h>//引入计算工具所有的头文件
#import <BaiduMapAPI_Radar/BMKRadarComponent.h>//引入周边雷达功能所有的头文件
#import <BaiduMapAPI_Map/BMKMapView.h>//只引入所需的单个头文件
具体的应用位置,一般是两个地方,一个是AppDelegate文件,一个是调用定位功能的ViewController文件。接下来两节会讲到怎么用。
3.AppDelegate文件配置
假设我现在的需求是,APP需要定位当前所在位置经纬度,并根据经纬度反地理编码,得到所在地址,包括省市区,街道等详细地址信息。
并假设,已经申请得到了一个密钥如下(拷这个没用,自己根据BundleID申请吧):
B266f735e43ab207ec152deff44fec8b
首先,需要在AppDelegate.mm文件导入所需头文件:
//百度地图
#define BMK_KEY @"B266f735e43ab207ec152deff44fec8b"//百度地图的key
#import <BaiduMapAPI_Base/BMKBaseComponent.h>//引入base相关所有的头文件
其次,声明一个BMKMapManager属性:
@interface AppDelegate ()
@property (nonatomic, strong) BMKMapManager *mapManager;
@end
然后,在AppDelegate的- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {}
代理方法中初始化百度地图,代码如下:
//百度地图
_mapManager = [BMKMapManager new];
BOOL ret = [_mapManager start:BMK_KEY generalDelegate:nil];
if (!ret)
{
NSLog(@"百度地图启动失败");
}
else
{
NSLog(@"百度地图启动成功");
}
这样,当APP启动就会启动百度地图相关模块,以便后续VC调用。
4.用到百度定位功能所在VC的配置
首先,导入相关头文件,及key的宏定义
//百度地图
#import <BaiduMapAPI_Base/BMKBaseComponent.h>//引入base相关所有的头文件
#import <BaiduMapAPI_Location/BMKLocationComponent.h>//引入定位功能所有的头文件
#import <BaiduMapAPI_Search/BMKSearchComponent.h>//引入检索功能所有的头文件
#define BMK_KEY @"B266f735e43ab207ec152deff44fec8b"//百度地图的key
其次,声明该VC服从百度相关模块的代理如下,其它代理自选:
//百度地图·代理
@interface TestViewController ()<UITableViewDataSource, UITableViewDelegate,CLLocationManagerDelegate,BMKGeneralDelegate,BMKLocationServiceDelegate,BMKGeoCodeSearchDelegate>
{
}
然后,声明相关属性如下:
//百度地图
@property (nonatomic, strong)BMKLocationService *locService;
@property (nonatomic, strong)BMKGeoCodeSearch *geocodesearch;
@property BOOL isGeoSearch;
接着,在VC的- (void)viewDidLoad {}
方法里面,对相关模块进行初始化操作。
//百度地图
//启动LocationService
_locService = [[BMKLocationService alloc]init];//定位功能的初始化
_locService.delegate = self;//设置代理位self
[_locService startUserLocationService];//启动定位服务
最后,实现百度地图相关代理方法,并进行自定义的一些操作。
#pragma mark - BMK_LocationDelegate 百度地图
/**
*定位失败后,会调用此函数
*@param error 错误号
*/
- (void)didFailToLocateUserWithError:(NSError *)error
{
NSLog(@"地图定位失败======%@",error);
}
//处理位置坐标更新
- (void)didUpdateBMKUserLocation:(BMKUserLocation *)userLocation
{
NSLog(@"didUpdateUserLocation lat %f,long %f",userLocation.location.coordinate.latitude,
userLocation.location.coordinate.longitude);
//从manager获取左边
CLLocationCoordinate2D coordinate = userLocation.location.coordinate;//位置坐标
//存储经纬度
[self.userLocationInfoModel SaveLocationCoordinate2D:coordinate];
if ((userLocation.location.coordinate.latitude != 0 || userLocation.location.coordinate.longitude != 0))
{
//发送反编码请求
//[self sendBMKReverseGeoCodeOptionRequest];
NSString *latitude = [NSString stringWithFormat:@"%f",userLocation.location.coordinate.latitude];
NSString *longitude = [NSString stringWithFormat:@"%f",userLocation.location.coordinate.longitude];
[self reverseGeoCodeWithLatitude:latitude withLongitude:longitude];
}else{
NSLog(@"位置为空");
}
//关闭坐标更新
[self.locService stopUserLocationService];
}
//地图定位
- (BMKLocationService *)locService
{
if (!_locService)
{
_locService = [[BMKLocationService alloc] init];
_locService.delegate = self;
}
return _locService;
}
//检索对象
- (BMKGeoCodeSearch *)geocodesearch
{
if (!_geocodesearch)
{
_geocodesearch = [[BMKGeoCodeSearch alloc] init];
_geocodesearch.delegate = self;
}
return _geocodesearch;
}
#pragma mark ----反向地理编码
- (void)reverseGeoCodeWithLatitude:(NSString *)latitude withLongitude:(NSString *)longitude
{
//发起反向地理编码检索
CLLocationCoordinate2D coor;
coor.latitude = [latitude doubleValue];
coor.longitude = [longitude doubleValue];
BMKReverseGeoCodeOption *reverseGeocodeSearchOption = [[BMKReverseGeoCodeOption alloc] init];
reverseGeocodeSearchOption.reverseGeoPoint = coor;
BOOL flag = [self.geocodesearch reverseGeoCode:reverseGeocodeSearchOption];;
if (flag)
{
NSLog(@"反地理编码成功");//可注释
}
else
{
NSLog(@"反地理编码失败");//可注释
}
}
//发送反编码请求
- (void)sendBMKReverseGeoCodeOptionRequest{
self.isGeoSearch = false;
CLLocationCoordinate2D pt = (CLLocationCoordinate2D){0, 0};//初始化
if (_locService.userLocation.location.coordinate.longitude!= 0
&& _locService.userLocation.location.coordinate.latitude!= 0) {
//如果还没有给pt赋值,那就将当前的经纬度赋值给pt
pt = (CLLocationCoordinate2D){_locService.userLocation.location.coordinate.latitude,
_locService.userLocation.location.coordinate.longitude};
}
BMKReverseGeoCodeOption *reverseGeocodeSearchOption = [[BMKReverseGeoCodeOption alloc]init];//初始化反编码请求
reverseGeocodeSearchOption.reverseGeoPoint = pt;//设置反编码的店为pt
BOOL flag = [_geocodesearch reverseGeoCode:reverseGeocodeSearchOption];//发送反编码请求.并返回是否成功
if(flag)
{
NSLog(@"反geo检索发送成功");
}
else
{
NSLog(@"反geo检索发送失败");
}
}
//发送成功,百度将会返回东西给你
-(void)onGetReverseGeoCodeResult:(BMKGeoCodeSearch *)searcher
result:(BMKReverseGeoCodeResult *)result
errorCode:(BMKSearchErrorCode)error
{
if (error == BMK_SEARCH_NO_ERROR) {
NSString *address1 = result.address; // result.addressDetail ///层次化地址信息
NSLog(@"我的位置在 %@",address1);
//保存位置信息到模型
[self.userLocationInfoModel saveLocationInfoWithBMKReverseGeoCodeResult:result];
//进行缓存处理,上传到服务器等操作
}
需要提到的是,可以合理利用BMKReverseGeoCodeResult
类型的对象result
,比如取出它的某些属性存到自己定义的模型中去。该对象的类型是百度地图SDK的类,里面包含了根据经纬度返回的地址信息。它的类文件如下:
/*
* BMKGeocodeType.h
* BMapKit
*
* Copyright 2011 Baidu Inc. All rights reserved.
*
*/
#import <BaiduMapAPI_Base/BMKTypes.h>
///反地址编码结果
@interface BMKReverseGeoCodeResult : NSObject
{
BMKAddressComponent* _addressDetail;
NSString* _address;
CLLocationCoordinate2D _location;
NSArray* _poiList;
}
///层次化地址信息
@property (nonatomic, strong) BMKAddressComponent *addressDetail;
///地址名称
@property (nonatomic, strong) NSString* address;
///商圈名称
@property (nonatomic, strong) NSString* businessCircle;
///结合当前位置POI的语义化结果描述
@property (nonatomic, strong) NSString* sematicDescription;
///地址坐标
@property (nonatomic) CLLocationCoordinate2D location;
///地址周边POI信息,成员类型为BMKPoiInfo
@property (nonatomic, strong) NSArray* poiList;
@end
///地址编码结果
@interface BMKGeoCodeResult : NSObject
{
CLLocationCoordinate2D _location;
NSString* _address;
}
///地理编码位置
@property (nonatomic) CLLocationCoordinate2D location;
///地理编码地址
@property (nonatomic,strong) NSString* address;
@end
其中,result
对象包含了一个层次化地址信息,如@property (nonatomic, strong) BMKAddressComponent *addressDetail;
所示。它的addressDetail
属性包含的信息可从BMKAddressComponent
类的代码了解更多:
///线路检索节点信息,一个路线检索节点可以通过经纬度坐标或城市名加地名确定
@interface BMKPlanNode : NSObject{
NSString* _cityName;
NSString* _name;
CLLocationCoordinate2D _pt;
}
///节点所在城市
@property (nonatomic, strong) NSString* cityName;
///节点所在城市ID
@property (nonatomic, assign) NSInteger cityID;
///节点名称
@property (nonatomic, strong) NSString* name;
///节点坐标
@property (nonatomic) CLLocationCoordinate2D pt;
@end
///室内路线检索节点信息
@interface BMKIndoorPlanNode : NSObject
///节点所在楼层
@property (nonatomic, retain) NSString* floor;
///节点坐标
@property (nonatomic) CLLocationCoordinate2D pt;
@end
///此类表示地址结果的层次化信息
@interface BMKAddressComponent : NSObject
/// 街道号码
@property (nonatomic, strong) NSString* streetNumber;
/// 街道名称
@property (nonatomic, strong) NSString* streetName;
/// 区县名称
@property (nonatomic, strong) NSString* district;
/// 城市名称
@property (nonatomic, strong) NSString* city;
/// 省份名称
@property (nonatomic, strong) NSString* province;
/// 国家
@property (nonatomic, strong) NSString* country;
/// 国家代码
@property (nonatomic, strong) NSString* countryCode;
@end
正如上面的代码中看到,里面包含了国家,省市区,街道及街道号等层次化信息。在实际的开发中,可以按需取出这些信息,这种操作可以写在下面的代理方法中去。
//发送成功,百度将会返回东西给你
-(void)onGetReverseGeoCodeResult:(BMKGeoCodeSearch *)searcher
result:(BMKReverseGeoCodeResult *)result
errorCode:(BMKSearchErrorCode)error
{
//取出层次化信息操作
}
附1:可能的问题
百度地图反geo检索发送失败
可能因为,key是其它样例Demo的,或者以前申请的过期了。检查一下,用自己工程的Bundle Identifer重新申请key,在真机上进行测试,反检索发起成功。这时候需要重新申请密钥key。