1. 程式人生 > >iOS·採用第三方(百度地圖SDK)實現定位等功能開發

iOS·採用第三方(百度地圖SDK)實現定位等功能開發

96 陳滿iOS 22d8d123 271c 4d80 9c59 6990844a9e37 關注

2017.05.01 01:06* 字數 2212 閱讀 6818評論 7喜歡 133

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