iOS之集成GoogleMap(定位、搜索)需要注意的事

简介:

最近花了些时间看了GoogleMap官方文件并集成到国际版app中,网上关于GoogleMap for iOS的讲解相对Android来说少一点,比较有帮助的几乎全是英文文档。下面是我开发过程中遇到的坑、以及采用的解决方法。

集成GoogleMap步骤:

1、Cocoapods导入pod 'GoogleMaps'
2、获取API密匙(前提是已经在GoogleMapSDK中创建好自己的应用)
3、配置plist文件搭建定位环境
4、调用代理方法实现需求

tips:pod 'GoogleMaps'、pod 'GooglePlaces'、pod 'GooglePlacePicker'这三个框架。(GoogleMaps:显示基本的定位功能;GooglePlaces:实现搜索功能,官方文档叫做地点自动完成;GooglePlacePicker:是实现获取某个POI的的详细信息,比如名字、详细地址、路线等)

景点(POI)包括公园、学校和政府大楼,等等。 另外,如果地图类型为 kGMSTypeNormal,商家景点默认将显示在地图上。 商家景点表示商店、餐馆和酒店之类的商家。
按照 Google Places API 中的定义,一个 POI 对应于一个地点。 例如,休闲公园为景点,但喷泉之类的地点通常不属于景点(除非它们具有国家或历史意义)。

配置plist文件:

打开plist的代码源文件,输入:
配置plist属性搭建定位环境

定位:

一、在AppDelegate 头文件 导入框架

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

二、向您的 application:didFinishLaunchingWithOptions: 方法添加以下内容,使用我们刚才获取到的 API 密钥替代 YOUR_API_KEY:

[GMSServices provideAPIKey:@"YOUR_API_KEY"];

tips:这一步是在启动app的时候,GoogleMap准备代理工作。
三、在我们需要显示地图的控制器调用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 控件样式属性

到这里,搜索功能就算完成了。
这里是官方中文文档 GoogleMap for iOS
这里是官方中文文档 Google Places API

谢谢~

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,884评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,347评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,435评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,509评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,611评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,837评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,987评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,730评论 0 267
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,194评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,525评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,664评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,334评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,944评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,764评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,997评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,389评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,554评论 2 349