简介:

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

集成GoogleMap步骤:

1、Cocoapods导入

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

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的代码源文件,输入:

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

定位:

一、在AppDelegate 头文件 导入框架

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

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

[GMSServices provideAPIKey:@"YOUR_API_KEY"]; //启动Google地图

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 控件样式属性

到这里,搜索功能就算完成了。

封装搜索弹框视图:

效果图:

.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

iOS之集成GoogleMap定位、搜索注意事项的更多相关文章

  1. 现有iOS项目集成React Native过程记录

    在<Mac系统下React Native环境搭建>配置了RN的开发环境,然后,本文记录在现有iOS项目集成React Native的过程,官方推荐使用Cocoapods,项目一开始也是使用 ...

  2. 李洪强iOS之集成极光推送二iOS 证书 设置指南

    李洪强iOS之集成极光推送二iOS 证书 设置指南 创建应用程序ID 登陆 iOS Dev Center 选择进入iOS Provisioning Portal. 在 iOS Provisioning ...

  3. 李洪强iOS之集成极光推送一iOS SDK概述

    李洪强iOS之集成极光推送一iOS SDK概述 JPush iOS 从上图可以看出,JPush iOS Push 包括 2 个部分,APNs 推送(代理),与 JPush 应用内消息. 红色部分是 A ...

  4. fir.im Weekly - 暖心的 iOS 持续集成,你值得拥有

    一则利好消息,flow.ci 支持 iOS 项目持续集成,想试试的伙伴去 Gitter群 问问.首批尝鲜用户@阿米amoy 已经用 flow.ci 实现了基本的 iOS 持续集成,并详细记录整个 Bu ...

  5. 如何在ios中集成微信登录功能

    在ios中集成微信的登录功能有两种方法 1 用微信原生的api来做,这样做的好处就是轻量级,程序负重小,在Build Settings 中这样设置 然后设置 友盟的设置同上,但是要注意,加入你需要的所 ...

  6. 使用 Fastlane 实现 IOS 持续集成

    简介 持续集成是个“一次配置长期受益”的工作.但很多小公司都没有.以前在做Windows开发配置感觉简单一些,这次配置iOS的,感觉步骤还挺多.整理出来,分享给大家,不正确的地方请及时指正. 本文主要 ...

  7. 使用VSTS/TFS搭建iOS持续集成环境

    TFS 自2015版开始支持跨平台的持续集成环境,通过提供开源的build agent为 Windows / linux / macOS 提供了统一的持续集成环境管理能力.这篇文章给大家介绍一下如何使 ...

  8. 环信 之 iOS 客户端集成四:集成UI

    在Podfile文件里加入 pod 'EaseUI', :git => 'https://github.com/easemob/easeui-ios-cocoapods.git' 然后在终端中的 ...

  9. 【iOS】7.4 定位服务->2.1.3.1 定位 - 官方框架CoreLocation 功能1:地理定位

    本文并非最终版本,如果想要关注更新或更正的内容请关注文集,联系方式详见文末,如有疏忽和遗漏,欢迎指正. 本文相关目录: ================== 所属文集:[iOS]07 设备工具 === ...

随机推荐

  1. Dubbo自定义Filter统一处理异常

    Dubbo版本:2.7 使用自定义Filter注意事项 1.自定义名称不能和默认Filter相同,否则可能不生效 2.只用定义Filter类和META-INF下的文本文件,不用添加配置,@Activa ...

  2. KMS服务器激活

    https://blog.csdn.net/weixin_42588262/article/details/81120403 http://kms.cangshui.net/ https://kms. ...

  3. Xamarin.FormsShell基础教程(9)Shell相关类体系

    Xamarin.FormsShell基础教程(9)Shell相关类体系 在Shell中,最为主要的类是Shell类.Shell类实现了大多数应用程序所需的基本UI功能的页面.除此以外,常用的类还有Sh ...

  4. row_number() over()分组排序功能 partition by 用于给结果集分组

    select * from ( select row_number() over(partition by Gid order by Gid ASC) as RowN, * from( select ...

  5. Java13新特性 -- ZGC:取消使用未使用的内存

    在JDK 11中,Java引入了ZGC,这是一款可伸缩的低延迟垃圾收集器,但是当时只是实验性的.号称不管你开了多大的堆内存,它都能保证在 10 毫秒内释放 JVM ,不让它停顿在那.但是,当时的设计是 ...

  6. cisco路由器telnet及设置用户名和密码的几种方式

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明.本文链接:https://blog.csdn.net/sxajw/article/details ...

  7. Ubuntu 安装docker CE以及harbor

    Docker CE安装 系统建议版本:Ubuntu 16.04 官方安装文档连接:https://docs.docker.com/install/linux/docker-ce/ubuntu/#pre ...

  8. CMDBuild部署教程

    一.CMDBuild简介 CMDBuild是一个通过Web界面配置的CMDB系统.可以通过Web界面来进行建模.创建资产数据库,并处理相关的工作流程.CMDBuild可用于集中管理数据库模块和外部应用 ...

  9. DApp是什么,DApp是必然趋势

    DApp是什么,DApp是必然趋势  https://www.jianshu.com/p/dfe3098de0de Thehrdertheluck关注 12018.04.23 11:54:00字数 2 ...

  10. 【Docker学习之三】Docker查找拉取镜像、启动容器、容器使用

    环境 docker-ce-19.03.1-3.el7.x86_64 CentOS 7 一.查找.拉取镜像.启动容器1.查找镜像-docker search默认查找Docker Hub上的镜像,举例:D ...