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.png

2.工程配置

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。

附2:参考文献

  1. 百度地图iOS SDK开发指南
  2. 百度地图iOS地图SDK

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏iOS开发攻城狮的集散地

iOS CoreData (二) 版本升级和数据库迁移

NSMigratePersistentStoresAutomaticallyOption = YES,那么Core Data会试着把之前低版本的出现不兼容的持久...

26710
来自专栏糊一笑

react脚手架改造(react/react-router/redux/eslint/karam/immutable/es6/webpack/Redux DevTools)

公司突然组织需要重新搭建一个基于node的论坛系统,前端采用react,上网找了一些脚手架,或多或少不能满足自己的需求,最终在基于YeoMan的react脚手架...

3825
来自专栏菩提树下的杨过

ActiveMQ笔记(6):消息延时投递

在开发业务系统时,某些业务场景需要消息定时发送或延时发送(类似:飞信的短信定时发送需求),这时候就需要用到activemq的消息延时投递,详细的文档可参考官网说...

4225
来自专栏Seebug漏洞平台

使用 XML 内部实体绕过 Chrome 和 IE 的 XSS 过滤器

来源:BypassingXSSFiltersusingXMLInternalEntities 原作者:DavidLitchfield (david@davidl...

40710
来自专栏陈满iOS

报错笔记:Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)

https://stackoverflow.com/questions/24337791/exc-bad-instruction-code-exc-i386-i...

2.1K2
来自专栏程序员的SOD蜜

使用“消息服务框架”(MSF)实现分布式事务的三阶段提交协议(电商创建订单的示例)

1,示例解决方案介绍 在上一篇 《消息服务框架(MSF)应用实例之分布式事务三阶段提交协议的实现》中,我们分析了分布式事务的三阶段提交协议的原理,现在我们来看看...

3379
来自专栏LinXunFeng的专栏

iOS - FTPManager的简单使用及常见问题

1744
来自专栏数据之美

使用 django-blog-zinnia 搭建个人博客

django-blog-zinnia 虽然小巧,但是具备了个人博客应用的全部基础功能,且具有很高的拓展性,并且开箱即用。以下是官方列出的一些特性: 评论 站...

3329
来自专栏NetCore

Identity Service - 解析微软微服务架构eShopOnContainers(二)

接上一篇,众所周知一个网站的用户登录是非常重要,一站式的登录(SSO)也成了大家讨论的热点。微软在这个Demo中,把登录单独拉了出来,形成了一个Service,...

2695
来自专栏Golang语言社区

gRPC服务发现&amp;负载均衡

构建高可用、高性能的通信服务,通常采用服务注册与发现、负载均衡和容错处理等机制实现。根据负载均衡实现所在的位置不同,通常可分为以下三种解决方案:

8252

扫码关注云+社区

领取腾讯云代金券