iOS之集成GoogleMap定位、搜索注意事項

簡介:

最近花了些時間看了GoogleMap官方文件並集成到國際版app中,網上關於GoogleMap for iOS的講解相對Android來講少一點,比較有幫助的幾乎全是英文文檔。下面是我開發過程當中遇到的坑、以及採用的解決方法。html

集成GoogleMap步驟:

一、Cocoapods導入ios

   pod 'GoogleMaps', '~> 2.7.0'  #谷歌地圖
   pod 'GooglePlaces', '= 2.7.0'
   pod 'GooglePlacePicker', '= 2.7.0'

二、獲取API密匙(前提是已經在GoogleMapSDK中建立好本身的應用)git

三、配置plist文件搭建定位環境api

四、調用代理方法實現需求數組

tips:pod 'GoogleMaps'、pod 'GooglePlaces'、pod 'GooglePlacePicker'這三個框架。(GoogleMaps:顯示基本的定位功能;GooglePlaces:實現搜索功能,官方文檔叫作地點自動完成;GooglePlacePicker:是實現獲取某個POI的的詳細信息,好比名字、詳細地址、路線等)app

景點(POI)包括公園、學校和政府大樓,等等。 另外,若是地圖類型爲 kGMSTypeNormal,商家景點默認將顯示在地圖上。 商家景點表示商店、餐館和酒店之類的商家。
按照 Google Places API 中的定義,一個 POI 對應於一個地點。 例如,休閒公園爲景點,但噴泉之類的地點一般不屬於景點(除非它們具備國家或歷史意義)。框架

配置plist文件:

打開plist的代碼源文件,輸入:ide

<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>XXX needs to use your location information to provide location services</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>XXX needs to use your location information to provide location services</string>
<key>NSLocationWhenInUseUsageDescription - 2</key>
<string>XXX needs to use your location information to provide location services</string>

定位:

1、在AppDelegate 頭文件 導入框架ui

#import <GoogleMaps/GoogleMaps.h>
#import <CoreLocation/CoreLocation.h>

 2、向您的 application:didFinishLaunchingWithOptions: 方法添加如下內容,使用咱們剛纔獲取到的 API 密鑰替代 YOUR_API_KEY:google

[GMSServices provideAPIKey:@"YOUR_API_KEY"]; //啓動Google地圖

 tips:這一步是在啓動app的時候,GoogleMap準備代理工做。
3、在咱們須要顯示地圖的控制器調用API方法

@property (nonatomic,strong) CLLocationManager *locationManager;//地圖定位對象
@property (nonatomic,strong) GMSMapView *mapView;//地圖
@property (nonatomic,strong) GMSMarker *marker;//大頭針
@property (nonatomic,strong) GMSPlacesClient * placesClient;//能夠獲取某個地方的信息
//註冊的代理
@interface TestMapViewController ()
<CLLocationManagerDelegate,GMSMapViewDelegate,GMSAutocompleteViewControllerDelegate>  

 

tips:這是在控制器.h文件聲明的屬性。
(一)初始化一個地圖對象

GMSMapView:是控制地圖的外觀類
GMSCameraPosition:是控制地圖要顯示的內容類

 GMSCameraPosition *camera = [GMSCameraPosition cameraWithLatitude:-23.12960481
                                                            longitude:113.30887721
                                                                 zoom:Level];
    self.mapView = [GMSMapView mapWithFrame:CGRectZero camera:camera];
    self.mapView.delegate = self; //註冊代理屬性
    self.mapView.settings.compassButton = YES;//顯示指南針
    [self.view addSubview:self.mapView];
tips:上面的經緯度能夠隨便傳一個,以後會獲取到新的經緯度並更新位置。Level是地圖的比例伸縮度,值越大,地圖的拉伸就越大。

(二)初始化一個定位管理者對象

if (self.locationManager == nil) {
        self.locationManager = [[CLLocationManager alloc]init];
    }
    self.locationManager.delegate = self;
    [self.locationManager requestAlwaysAuthorization];//受權方式,若是在後臺也須要定位,那就選擇 requestAlwaysAuthorization。
    self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;//最精確的定位
    self.locationManager.distanceFilter = kCLDistanceFilterNone; // 默認是kCLDistanceFilterNone,也能夠設置其餘值,表示用戶移動的距離小於該範圍內就不會接收到通知
    [self.locationManager startUpdatingLocation];

tips:CLLocationManager 是負責獲取用戶行爲的類,列如獲取用戶當前位置信息。更多詳細信息請閱覽CLLocationManager。裏面講解CLLocationManager的一些應用場景並有代碼實例。
運行app:這時候咱們會看到並無實景地圖出來,緣由是:前面提到的GMSCameraPosition類,咱們並無在定位成功以後將定位內容賦它。

GMSCameraPosition類,它是負責顯示定位內容的。很重要!

(三)在定位成功的API代理方法中,獲取經緯度並轉成影像賦值

- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations{

    CLLocation *curLocation = [locations lastObject];
    // 經過location  或獲得當前位置的經緯度
    CLLocationCoordinate2D curCoordinate2D = curLocation.coordinate;
    GMSCameraPosition *camera = [GMSCameraPosition cameraWithLatitude:curCoordinate2D.latitude longitude:curCoordinate2D.longitude zoom:Level];
    CLLocationCoordinate2D position2D = CLLocationCoordinate2DMake(curLocation.coordinate.latitude, curLocation.coordinate.longitude);
    self.mapView.camera = camera;//這句話很重要很重要,將咱們獲取到的經緯度轉成影像並賦值給地圖的camera屬性

    [self.locationManager stopUpdatingLocation];//定位成功後中止定位

}

 tips:locationManager: didUpdateLocations: 代理方法是GoogleMap 中實現定位成功後回調的代理方法,你能夠在這裏獲取到經緯度。若是代碼走不到這個代理方法,有多是plist文件沒有配置,或者沒有定義代理屬性。
運行app:這時候地圖就出來了

添加大頭針

GMSMarker類是負責顯示大頭針,默認是紅色,你能夠自定義大頭針,用圖片或者改變顏色,具體看官方文檔GMSMarker

 self.marker = [GMSMarker markerWithPosition:position2D];
 self.marker.map = self.mapView;

tips:position2D是在定位成功以後轉換獲得的CLLocationCoordinate2D屬性經緯度值。

小坑提示:這時候有可能會出現,定位成功以後出現多個大頭針。緣由是:進行定位的時候,map獲取多個預測位置,從而產生生成多個大頭針的現象。解決辦法:在每次生成大頭針以前先清除以前的那個,只生成最精準的最後一個。

  [self.marker.map clear];
    self.marker.map = nil;

反編碼(經緯度轉成具體位置):

    CLGeocoder *geocoder = [[CLGeocoder alloc]init];
    //反地理編碼
    [geocoder reverseGeocodeLocation:curLocation completionHandler:^(NSArray<CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error) {
        
        if (error) {
        }else{
        
              CLPlacemark *placemark = [placemarks objectAtIndex:0];//第一個位置是最精確的
                //賦值詳細地址
                DLog(@"placemark---路號name:%@-市locality:%@-區subLocality:%@-省administrativeArea:%@-路thoroughfare:%@",placemark.name,placemark.locality,placemark.subLocality,placemark.administrativeArea,placemark.thoroughfare);
           
      }];

這時候就已經能夠獲取到具體的國家、省、市、區、街道了。

補充:反編碼是獲取不到POI位置的(我獲取不到)。這時候可使用

 self.placesClient = [GMSPlacesClient sharedClient];//獲取某個地點的具體信息

    [self.placesClient currentPlaceWithCallback:^(GMSPlaceLikelihoodList *likelihoodList, NSError *error) {
        if (error != nil) {
            DLog(@"Current Place error %@", [error localizedDescription]);
            return;
        }
        
//        for (GMSPlaceLikelihood *likelihood in likelihoodList.likelihoods) {
//            GMSPlace* place = likelihood.place;
//            NSLog(@"Current Place name %@ at likelihood %g", place.name, likelihood.likelihood);
//            NSLog(@"Current Place address %@", place.formattedAddress);
//            NSLog(@"Current Place attributions %@", place.attributions);
//            NSLog(@"Current PlaceID %@", place.placeID);
//        }

         //這裏就能夠獲取到POI的名字了
         //這裏作一些你想作的事
        
    }];

 

點擊地圖並移動大頭針

這裏是用到GMSMapViewDelegate的代理回調
回調1:這裏是點擊地圖上的某個點API返回的代理方法,在這個代理方法,你能夠獲取經緯度去反編譯地址

- (void)mapView:(GMSMapView *)mapView didTapAtCoordinate:(CLLocationCoordinate2D)coordinate{

    //點擊一次先清除上一次的大頭針
    [self.marker.map clear];
    self.marker.map = nil;
    // 經過location  或獲得當前位置的經緯度
    GMSCameraPosition *camera = [GMSCameraPosition cameraWithLatitude:coordinate.latitude longitude:coordinate.longitude zoom:Level];
    CLLocationCoordinate2D position2D = CLLocationCoordinate2DMake(coordinate.latitude,coordinate.longitude);
    self.mapView.camera = camera;
    //大頭針
    self.marker = [GMSMarker markerWithPosition:position2D];
    self.marker.map = self.mapView;

    CLLocation *curLocation = [[CLLocation alloc]initWithLatitude:coordinate.latitude longitude:coordinate.longitude];
    
    CLGeocoder *geocoder = [[CLGeocoder alloc]init];
    //反地理編碼
    [geocoder reverseGeocodeLocation:curLocation completionHandler:^(NSArray<CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error) {
        
        if (error) {
            
            DLog(@"error.description:%@",error.description);
            
        }else{
            
            CLPlacemark *placemark = [placemarks objectAtIndex:0];
            
            //賦值詳細地址
            DLog(@"placemark---路號name:%@-市locality:%@-區subLocality:%@-省administrativeArea:%@-路thoroughfare:%@",placemark.name,placemark.locality,placemark.subLocality,placemark.administrativeArea,placemark.thoroughfare);
          
    }];
    
}

 回調2:這裏也是點擊地圖上的某個點API返回的代理方法

- (void)mapView:(GMSMapView *)mapView
didTapPOIWithPlaceID:(NSString *)placeID
           name:(NSString *)name
       location:(CLLocationCoordinate2D)location{
}

 

tips:值得注意的,二者的區別是:第二個點擊代理方法是當你點擊POI的時候纔會回調,會返回place的name、ID、經緯度;第一個代理方法是隻要點擊地圖任意一個位置就會回調,只會返回經緯度。也就是:每一次的點擊,只會執行其中一個代理方法。

搜索:

搜索功能在官方文檔是叫作「自動完成」,即你輸入一部分的文本,GoogleMap會根據你的文本預測出地點並自動填充返回,具體請看官方文檔自動完成

效果如圖:

這裏你須要作的步驟跟作「定位」的同樣:
(1)獲取APIKEY
(2) 在application:didFinishLaunchingWithOptions: 註冊密匙

[GMSPlacesClient provideAPIKey:@"YOUR_API_KEY"]; //地點:搜索功能

 (3) 建立搜索UI並調用代理方法獲取API自動填充的結果數組集
導入頭文件

#import <GooglePlaces/GooglePlaces.h>

小坑提示: GMSPlacesClient跟GMSServices的密匙是不同的,密匙不對的話,會出現反覆調用viewController:didFailAutocompleteWithError:的現象。

tips:搭建搜索UI有幾種方式:1)搜索框直接建立在導航欄 2)搜索欄建立在視圖頂部 3)自定義。根據你的需求用代碼~
(一)這裏是第一種方式(搜索框直接建立在導航欄):

    GMSAutocompleteViewController *acController = [[GMSAutocompleteViewController alloc] init];
    acController.delegate = self;
    [self presentViewController:acController animated:YES completion:nil];

 tips:這裏就能夠直接往搜索框編輯文字,API會直接給你返回搜索結果集合
(二)調用API代理方法:

// Handle the user's selection.  這是用戶選擇搜索中的某個地址後返回的結果回調方法
- (void)viewController:(GMSAutocompleteViewController *)viewController
didAutocompleteWithPlace:(GMSPlace *)place {
    
    [self dismissViewControllerAnimated:YES completion:nil];

    [self.marker.map clear];
    self.marker.map = nil;
    // 經過location  或獲得當前位置的經緯度
    GMSCameraPosition *camera = [GMSCameraPosition cameraWithLatitude:place.coordinate.latitude longitude:place.coordinate.longitude zoom:Level];
    CLLocationCoordinate2D position2D = CLLocationCoordinate2DMake(place.coordinate.latitude,place.coordinate.longitude);
    self.marker = [GMSMarker markerWithPosition:position2D];
    self.mapView.camera = camera;
    self.marker.map = self.mapView;
    
    self.locationLabel.text = place.name;
    self.locationDetailLabel.text = place.formattedAddress;
    
}

tips:這個代理方法實現的是,當用戶在搜索集中選擇了在某一個結果返回地圖,並定位添加大頭針。

自動填充失敗的回調:

- (void)viewController:(GMSAutocompleteViewController *)viewController
didFailAutocompleteWithError:(NSError *)error {
    [self dismissViewControllerAnimated:YES completion:nil];
    // TODO: handle the error.
    DLog(@"Error: %@", [error description]);
}

 

tips:自動填充失敗後你能夠在這裏作一些事,默認是無論的。
補充:搜索欄的外觀是能夠自定義的,你能夠設置成跟本身的app同樣的風格~具體請看設置 UI 控件樣式屬性

到這裏,搜索功能就算完成了。

封裝搜索彈框視圖:

效果圖:

.h文件

#import <UIKit/UIKit.h>

typedef void(^ReturnBackInfo)(id data);

NS_ASSUME_NONNULL_BEGIN

@interface ZYSerachAddressAlert : UIView

//選擇的地址信息回調
@property (nonatomic, copy) ReturnBackInfo backAddress;
//輸入的關鍵字
- (void)keyWordsChange:(NSString *)keywords;
//顯示
- (void)ShowAlert;
//隱藏
- (void)HideAlert;

@end

NS_ASSUME_NONNULL_END

 .m文件

#import "ZYSerachAddressAlert.h"
//google地圖
#import <GooglePlaces/GooglePlaces.h>

@interface ZYSerachAddressAlert() <GMSAutocompleteTableDataSourceDelegate>
//是否顯示
@property (nonatomic, assign) BOOL isShow;
/**谷歌地圖自送搜索數據源類*/
@property (nonatomic, strong) GMSAutocompleteTableDataSource *tableDataSource;

@property (nonatomic, strong) UITableView *tableView;

@end

@implementation ZYSerachAddressAlert

- (UITableView *)tableView {
    if (!_tableView) {
        _tableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain];
        _tableView.delegate = self.tableDataSource;
        _tableView.dataSource = self.tableDataSource;
    }
    return _tableView;
}

- (instancetype)initWithFrame:(CGRect)frame {
    if (self = [super initWithFrame:frame]) {
        
        CGFloat Y = [[UIApplication sharedApplication] statusBarFrame].size.height;
        
        CGFloat W = (kDeviceWidth) / 3.0;
        self.frame = CGRectMake(W + kWidthScale(16), Y, W -kWidthScale(33) , 208 - Y + kHeightScale(39) + 20);
        self.backgroundColor = [UIColor whiteColor];
        
        //google自動搜索數據源
        self.tableDataSource = [[GMSAutocompleteTableDataSource alloc] init];
        self.tableDataSource.delegate = self; //設置代理GMSAutocompleteTableDataSourceDelegate
        
        //限制搜索結果
        GMSAutocompleteFilter *filter = [[GMSAutocompleteFilter alloc] init];
        filter.country = @"IE"; //(只搜索愛爾蘭國的)
        self.tableDataSource.autocompleteFilter = filter;
        
        self.tableDataSource.tableCellBackgroundColor = [UIColor whiteColor];
        
        self.tableView.frame = self.bounds;
        [self addSubview:self.tableView];
    }
    return self;
}

- (void)ShowAlert {
    self.isShow = YES;
    self.hidden = NO;
    [[UIApplication sharedApplication].keyWindow addSubview:self];
}

- (void)HideAlert {
    self.isShow = NO;
    self.hidden = YES;
    [self removeFromSuperview];
}

- (void)dealloc {
    JGLog(@"銷燬了");
//    [self.tableView removeFromSuperview];
}

- (void)keyWordsChange:(NSString *)keywords {
    
    if (!self.isShow) {
        [self ShowAlert];
    }
    //輸入框內容發生變回就會觸發該方法
    //觸發搜索回調方法
    [self.tableDataSource sourceTextHasChanged:keywords];
}

#pragma mark -  GMSAutocompleteTableDataSourceDelegate
//!!!!:搜索
//@required點擊搜索結果代理上面的cell的時候會被調用
//要幹什麼:回收鍵盤,給textField賦值,隱藏搜索結果控制器
- (void)tableDataSource:(GMSAutocompleteTableDataSource *)tableDataSource
didAutocompleteWithPlace:(GMSPlace *)place {
    JGLog(@"xxxx:%@--%@",place.name,place.formattedAddress);
    
    [self HideAlert];
    
    if (self.backAddress) {
        self.backAddress(place);
    }
}

- (void)tableDataSource:(GMSAutocompleteTableDataSource *)tableDataSource
didFailAutocompleteWithError:(NSError *)error {
    JGLog(@"yyyy:%@",[error localizedDescription]);
}

//請求到數據的回調
//加載數據顯示菊花
- (void)didRequestAutocompletePredictionsForTableDataSource:
(GMSAutocompleteTableDataSource *)tableDataSource {
    
    [UIApplication sharedApplication].networkActivityIndicatorVisible = YES;

    [self.tableView reloadData];
}

//要幹什麼:回收鍵盤,隱藏菊花
- (void)didUpdateAutocompletePredictionsForTableDataSource:(GMSAutocompleteTableDataSource *)tableDataSource {
    [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
    [self.tableView reloadData];
}

/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
    // Drawing code
}
*/

@end

 

這裏是官方中文文檔  GoogleMap for iOS
這裏是官方中文文檔 Google Places API

相關文章
相關標籤/搜索