iOS Google地图开发小结(2017)

最近接触了一个international的项目, 用到Google地图, 在此稍微总结一下, 方便以后使用;

一. 准备工作

相关资料:
Google地图API
官方Google地图API

SMCalloutView
这个是点击大头针弹出信息框的自定义视图, 官方地图自定义有局限性, 下面👇会讲到, 示例如图:

自定义视图

二.开发内容

对于iOS开发者, Google地图提供了两个开发入口:
Maps SDK for iOS
添加 Google 地图

Places API for iOS
添加位置的最新信息

1.Places API for iOS

这个一般开发Google地图用不到, 不过还是简单说一下, 官方demo截图;

Places API官方demo截图

section0: (Autocomplete)


Autocomplete

Autocomplete主要功能是搜索相关的位置信息(文本方式);
里面还会展示UISearchBar/UISearchViewController的几种呈现方法, 感兴趣的可以看看;

section1: (Programmatic APIs)

Programmatic APIs

Programmatic APIs主要展示搜索或所选点附近的信息(地图方式);

2.Maps SDK for iOS

重点介绍内容, mark, mark....
还是一样, 先看下官方demo, 大概浏览一下Google地图的功能;

//基本组成部分
Map(地图): 基本的地图创建, 组件, 类别等;
Panorama(全景): 固定/可旋转的街景;
Overlays(覆盖物): 地图视图的自定义(地图大头针, 弹出信息框等);
Camera(摄像头): 当前地图的可视范围(设置摄像头中心点坐标、镜头缩放比例、方向、视角等);
Services(服务): 地理编码/逆地理编码;

结构概览:


Maps SDK Demos 结构概览
2.1 常用类介绍:
GMSMapView 最主要的地图类
GMSCameraPosition 地图摄像头,可以理解为当前地图的可视范围,可以获取到摄像头中心点坐标、镜头缩放比例、方向、视角等参数
GMSMarker 地图大头针
GMSGeocoder 反向地理编码类
GMSAddress 反向地理编码返回的类,包含坐标及地理位置描述等信息
CLLocationManager 就是CoreLocation框架下的地理位置管理类
GMSAutocompleteFetcher 搜索自动补全抓取器,通过该类的代理方法实现搜索自动补全
2.2 常用方法介绍:

GMSMapViewDelegate:

mapView:willMove: 镜头即将移动时调用
mapView:didChangeCameraPosition:镜头移动完成后调用mapView:didTapAtCoordinate: 点击地图时调用
mapView:didLongPressAtCoordinate: 长按地图时调用
mapView:didTapMarker: 点击大头针时调用
mapView:didTapInfoWindowOfMarker: 点击大头针的弹出视窗时调用
mapView:didLongPressInfoWindowOfMarker: 长按大头针视窗时调用
mapView:markerInfoWindow: 自定义大头针弹出视窗,返回UIView
mapView:didCloseInfoWindowOfMarker: 自定义大头针弹出视窗关闭时调用
mapView:didDragMarker: 拖拽大头针时调用
didTapMyLocationButtonForMapView: 点击定位大头针, 返回BOOL值
2.3 Google Maps URL 架构(单独介绍一下):

确实与国内不同, Google地图URL架构没在demo中提及;
👇介绍一下:


iOS 版 Google Maps 应用支持以下 URL 架构:

  • comgooglemaps://
    comgooglemaps-x-callback://
    – 这些架构允许您启动 iOS 版 Google Maps 应用,并执行下列几项操作之一:
    • 以指定的缩放级别显示指定位置的地图。
    • 搜索位置或地点,并将它们显示在地图上。
    • 请求从一个位置前往另一个位置的路线。 可以返回以下四种交通方式的路线:驾车、步行、骑自行车和乘坐公共交通工具。
    • 向应用添加导航。
    • 当应用完成后,使用 comgooglemaps-x-callback://
      发出一个回调。 回调经常用来使用户返回到最初打开 iOS 版 Google Maps 的应用。
  • comgooglemapsurl:// – 此架构允许您使用从桌面 Google Maps 网站得到的 URL 启动 iOS 版 Google Maps 应用。 这意味着您可以为用户提供原生移动体验,而不是简单地加载 Google Maps 网站。
    • 原始 URL 可以是 maps.google.com,或者 google.com/maps,也可以使用任何有效的国家代码顶级域名来代替 com。
    • 您还可以传递 goo.gl/maps 重定向 URL。
      您可以将 x-source 和 x-success 参数与 comgooglemapsurl:// URL 架构结合使用来发出回调。

2.3.1 检查设备上是否已安装 Google Maps 应用;

if ([[UIApplication sharedApplication] canOpenURL:
     [NSURL URLWithString:@"comgooglemaps://"]]) {
      //已安装Google地图APP
} else {
     //未安装Google地图APP
}

2.3.2 显示地图
参数:

  • center:这是地图视口中心点。 其格式为用逗号分隔的字符串 latitude,longitude
  • mapmode:设置所显示地图的种类。 可以设置为:standard 或 streetview。 如果未指定,则将使用当前的应用设置。
  • views:开启/关闭特定视图。 可以设置为:satellitetraffictransit。 可以使用逗号分隔符来设置多个值。 如果指定了不带任何值的参数,那么将清除所有的视图。
  • zoom:指定地图的缩放级别。
//示例 URL,它以纽约为中心、采用 14 级缩放级别来显示地图,且开启了交通视图
comgooglemaps://?center=40.765819,-73.975866&zoom=14&views=traffic

2.3.3 搜索
参数:

  • q:用于搜索的查询字符串。
//示例 URL 用来在指定位置附近搜索“Pizza"
comgooglemaps://?q=Pizza&center=37.759748,-122.427135

2.3.4 显示路线
参数:

  • saddr:设置路线搜索的起点。 它可以是一个纬度、经度或查询格式的地址。 如果它是返回多个结果的查询字符串, 将选择第一个结果。 如果该值留空,那么将使用该用户的当前位置。
  • daddr:设置路线搜索的终点。 具有与 saddr 相同的格式和行为。
  • directionsmode:交通方式。 可以设置为:drivingtransitbicyclingwalking
// 示例 URL 用来显示 Google 纽约办事处与肯尼迪国际机场之间的交通路线
comgooglemaps://?saddr=Google+Inc,+8th+Avenue,+New+York,+NY&daddr=John+F.+Kennedy+International+Airport,+Van+Wyck+Expressway,+Jamaica,+New+York&directionsmode=transit

2.3.5 指定回调URL
参数:

  • x-source – 发送 x-callback 请求的应用的名称。 最好使用短名称。
  • x-success – 完成时调用的 URL。 通常,这是您自己的应用的 URL 架构,可以让用户返回到原来的应用。
// 示例将启动 iOS 版 Google Maps 应用,并以纽约为中心显示地图。 该应用还会显示标有“SourceApp”的按钮。 当点击“SourceApp”按钮时,iOS 版 Google Maps 应用将发出一个指向虚拟的 URL 架构的回调, sourceapp://?resume=true.
comgooglemaps-x-callback://?center=40.765819,-73.975866&zoom=14
  &x-success=sourceapp://?resume=true
  &x-source=SourceApp

2.3.6 向应用添加导航

// 代码展示了如何使用 comgooglemaps-x-callback:// 架构来请求路线,然后在您的用户准备就绪后返回到您的应用。 该代码将执行以下操作
NSURL *testURL = [NSURL URLWithString:@"comgooglemaps-x-callback://"];
if ([[UIApplication sharedApplication] canOpenURL:testURL]) {
  NSString *directionsRequest = @"comgooglemaps-x-callback://" +
      @"?daddr=John+F.+Kennedy+International+Airport,+Van+Wyck+Expressway,+Jamaica,+New+York" +
      @"&x-success=sourceapp://?resume=true&x-source=AirApp";
  NSURL *directionsURL = [NSURL URLWithString:directionsRequest];
  [[UIApplication sharedApplication] openURL:directionsURL];
} else {
  NSLog(@"Can't use comgooglemaps-x-callback:// on this device.");
}

该代码将执行以下操作:

  • 验证 comgooglemaps-x-callback:// URL 架构是否可用。
  • 启动 iOS 版 Google Maps 应用,并请求前往纽约市肯尼迪国际机场的路线。 将起始地址留空即可请求从用户的当前位置出发的路线。
  • 将标记为“AirApp”的按钮添加到 iOS 版 Google Maps 应用中。 该按钮标签由 x-source 参数定义。
  • 当用户点击返回按钮时,调用虚拟 URL 架构 sourceapp://,。

三.遇到的问题

开发中难免遇到一些稀奇古怪的问题, 这里就着我遇到的问题分享一下;

需求:
实现一个自定义的弹窗, 要求点击弹窗左边视图返回上一页, 点击右边视图跳转导航功能;

问题:
mapView:markerInfoWindow:中我自定义了一个气泡视图, 视图左右各一个按钮, 按钮的点击方法被屏蔽, 只响应了整个气泡视图的点击方法(mapView:didTapInfoWindowOfMarker:);

解决办法:
在试了n个方法后, 终于让我找到了SMCalloutView, github上的一个自定义气泡的三方, 可以自由定义左右,中间, 背景视图, 非常棒👍, O(∩_∩)O哈哈~

首先是基础操作:

#import <SMCalloutView/SMCalloutView.h>

static const CGFloat CalloutYOffset = 10.0f;

@interface ViewController ()
@property (strong, nonatomic) SMCalloutView *calloutView;
@property (strong, nonatomic) UIView *emptyCalloutView;
@end

之后初始化SMCalloutView, 创建一个空View;

- (void)viewDidLoad
{
        self.calloutView = [[SMCalloutView alloc] init];
    self.calloutView.contentView = [UIView new];
    
    self.emptyCalloutView = [[UIView alloc] initWithFrame:CGRectZero];
}

其次是GMSMapViewDelegate里面的设置:

- (UIView *)mapView:(GMSMapView *)mapView markerInfoWindow:(GMSMarker *)marker {
    CLLocationCoordinate2D anchor = marker.position;
    
    CGPoint point = [mapView.projection pointForCoordinate:anchor];
    
    self.calloutView.calloutOffset = CGPointMake(0, -CalloutYOffset);
    
    //SMCalloutView中contentView
    UIView *googleTipView = [UIView alloc] initWithFrame:CGRectMake(0, 0, 200, 50);
    
    //左方按钮
    UIButton *leftButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 100, 50)];
    [googleTipView addSubview:bView];
    [leftButton addTarget:self action:@selector(leftButtonClick:) forControlEvents:UIControlEventTouchUpInside];
    
    //右方按钮
    UIButton *rightButton = [[UIButton alloc] initWithFrame:CGRectMake(100, 0, 100, 50)];
    [googleTipView addSubview:bView];
    [rightButton addTarget:self action:@selector(rightButtonClick:) forControlEvents:UIControlEventTouchUpInside];
    
    //取消默认背景
    SMCalloutBackgroundView *calloutBgView = [[SMCalloutBackgroundView alloc] initWithFrame:CGRectZero];
    
    self.calloutView.backgroundView = calloutBgView;
    self.calloutView.contentView = googleTipView;
    
    self.calloutView.hidden = NO;
    
    CGRect calloutRect = CGRectZero;
    calloutRect.origin = point;
    calloutRect.size = CGSizeZero;
    
    [self.calloutView presentCalloutFromRect:calloutRect
                                      inView:mapView
                           constrainedToView:mapView
                                    animated:YES];
    
    return self.emptyCalloutView;
}

- (void)mapView:(GMSMapView *)pMapView didChangeCameraPosition:(GMSCameraPosition *)position {
    if (pMapView.selectedMarker != nil && !self.calloutView.hidden) {
        CLLocationCoordinate2D anchor = [pMapView.selectedMarker position];

        CGPoint arrowPt = self.calloutView.backgroundView.arrowPoint;
        CGPoint pt = [pMapView.projection pointForCoordinate:anchor];
        pt.x -= arrowPt.x;
        pt.y -= arrowPt.y + CalloutYOffset;

        self.calloutView.frame = (CGRect) {.origin = pt, .size = self.calloutView.frame.size };
    } else {
        self.calloutView.hidden = YES;
    }
}

- (void)mapView:(GMSMapView *)mapView didTapAtCoordinate:(CLLocationCoordinate2D)coordinate {
    self.calloutView.hidden = YES;
}

- (BOOL)mapView:(GMSMapView *)mapView didTapMarker:(GMSMarker *)marker {
    mapView.selectedMarker = marker;
    return YES;
}

结果就可以在点击方法里面尽情的调用了:

//左边按钮点击方法
- (void)leftButtonClick:(UIButton *)button{
    
}
//右边按钮点击方法
- (void)rightButtonClick:(UIButton *)button{
    
}

最后感谢这位网友的分享, 附上链接:
stackoverflow网友的分享

关于Google地图开发:
iOS Google地图SDK入门教程
iOS--谷歌地图相关功能的实现

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

推荐阅读更多精彩内容