AR定位导航定位与授时是种怎样的体验

Blippar发布了一款AR导航, 并不是我们常见的那种
未来 AR City 想要实现的功能
苹果和谷歌先后推出 AR 开发工具,不仅带动了整个行业的发展,还开始让传统行业悄悄发生变化,最为显著的便是 AR 营销了,而成立于 2011 年的英国公司 Blippar 最早也是因为 AR 广告成名,在 2015 年和 2016 年这家公司先后获得 4500 万美元和 5400 万美元的融资。
日前,Blippar 最新发布了一款 AR 导航应用 AR City,但跟我们常见的那种导航不太一样。
Blippar 在官网博客中写道,该应用涉及到 AR 和计算机视觉技术,可帮助用户导航全球 300 多个城市。特别是,在一些选定的地方,他们将城市视觉定位(Urban Visual Positioning)与 AR 结合,可实现比 GPS(全球定位系统) 更精准的定位。用户拿着手机对准身边的建筑物,即可实时呈现该建筑物的名字。
去年,雷锋网便发现,国内百度地图导航软件上线了 AR 模式,在我们外出实时导航时,可开启 AR 模式,虚拟的方向箭头叠加在真实的路面上,让我们清晰看到路面的情况。
手机上显示出建筑物的名称
这款 AR City(目前还是测试版) 应用又有什么不同呢?
雷锋网发现,Blippar 讲到 AR City 可实现的功能包括三个方面:
基本的 AR 导航:AR 模式下道路信息显示;
丰富的地图内容:用户所处位置的 AR 信息显示,如用户所在的街道,以及风景名胜;
城市视觉定位(Urban Visual Positioning,UVP):通过计算机视觉可识别、定位和判断方向信息。但该功能目前只能在伦敦市中心、旧金山和美国山景城地区使用。
博文特别介绍到,AR 导航功能可在任何地方使用,背后由苹果地图支持。他们使用 GPS 推算出你所在的位置,然后手机 VIO 可追踪到你的移动。Blippar 通过其创建的 ARCL 库把 GPS 和 VIO 整合到整个体验中,其中 VIO 是借助苹果的 ARKit 工具,GPS 则是借助于 Core Location(iOS SDK 中提供设备位置的框架)。
地图现在覆盖的范围达到全球 300 多个城市,显示的信息包括街道名字、建筑物名称和当地的风景名胜点,也就是说,当你分不清东南西北时,可以通过 AR City 得知身边建筑物的名称,跟传统的路线导航相结合,你会更清晰地看到自己所走的方向,这可是路痴的一大福利。
运用计算机视觉开发的 UVP系统
Blippar 表示所有的信息将整合到视觉知识图谱(Visual Konwledge Graph)中,未来他们还将把视觉知识图谱共享给第三方合作伙伴们。不过,AR City 只能识别眼前的建筑物和街道,无法识别那些被隐藏的建筑物。
在伦敦市中心、旧金山和美国山景城地区,AR City 中还加入了城市视觉定位功能(UVP),除了识别建筑物,该功能还提供更丰富的信息。UVP 比 GPS 的定位精确度更高,后者失误率约为 16。
雷锋网觉得,对于很多路痴来说,即便是分不清东南西北,借助标志性的建筑物,恐怕也不会迷失方向了。长期来看,Blippar 大概想要实现的 AR 版的大众点评,不管是旅游景点,亦或是餐厅,只要拿出手机便可识别真实环境中的周边信息,而这也将更加方便人们出行了。
责任编辑:
声明:该文观点仅代表作者本人,搜狐号系信息发布平台,搜狐仅提供信息存储空间服务。
致力免费为玩家、消费者提供国内最值得入手的新奇酷玩产品
读懂智能与未来!
今日搜狐热点iOS 增强现实教程:基于位置定位
更新说明:本教程由 Jean-Pierre Distler 升级至 Swift3 和 iOS 10。
增强现实是一种很酷的流行技术,你可以通过特定设备(比如iPhone 摄像头或者微软的 Hololens)来观察世界,这个设备会在真实世界的画面上叠加额外的信息。
我猜你也许看过标志物跟踪之类的 App,在这个 App 中,当你将摄像头指向某个标志物时,会立即弹出一个 3D 模型。
在这篇 iOS 增强现实教程中,我们会编写一个 App,利用用户当前位置来找出附近的兴趣点(即 POI)。你可以将这些兴趣点添加到 MapView 上,并在镜头图像中显示它们。
要找出 POI,我们可以使用 Google 的 Places API,然后用 HDAugmentedReality 库将 POI 放到镜头视图中,并计算出用户当前位置到 POI 的距离。
本教程假设你熟悉 MapKit。如果你没用过 MapKit,请先阅读我们的。
请先下载并自行熟悉其中的内容。在项目导航窗口中选中 Places 项目,在 Target 的 General 窗口中,找到 Siging 栏,将 Team 修改为你自己的开发者账号。然后就可以编译项目了。Main.storyboard 中有一个 Scene 包含了一个 MapView 和一个 UIButton 按钮,它们都已经正确连接了。HDAugmentedReality 库已经导入,另外还有两个 Swift 文件:PlacesLoader.swift 和 Place.swift。这两个类后面我们会用来通过 Google 的 Places API 搜索兴趣点并将搜索结果映射成便于使用的对象。
在开始下一步之前,我们首先需要获取用户当前位置。也就是使用 CLLocationManager。打开 ViewController.swift 在 mapView 属性下面添加一个 locationManager 属性。
fileprivate let locationManager = CLLocationManager()
这里我们用一个 CLLocationManager 对象来初始化 locationManager。
为 ViewController 添加下列扩展:
在获取用户位置之前必须在 Info.plist 中添加一个 key。打开 Info.plist 增加一个键值对,键名为 NSLocationWhenInUseUsageDescription 键值为 a value of Needed for AR。当你第一次访问 iOS 的定位服务时,会显示一个对话框要求用户授权。
准备好这一切之后,我们来获取用户位置。打开 ViewController.swift ,将 viewDidLoad() 修改为:
override func viewDidLoad() {
super.viewDidLoad()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
locationManager.startUpdatingLocation()
locationManager.requestWhenInUseAuthorization()
locationManager 就基本配置好了。locationManager 需要一个委托对象,这样当设备位置发生变化时它会通知委托对象。这里我们将委托设置为 View Controller 自身。此外还需要指定定位所需的精度。这里我们设置为 KCLLocationAccuracyNearestTenMeters,对于本项目来说这个精度就够了。最后2行是打开 locationManager 以及向用户获取授权,如果用户还没有进行授权或者拒绝授权的话。
注意:对于 desiredAccuracy 属性,你应该根据使用目的选择能够满足需要的最低精度就可以了。为什么?
假设你只需要百米级别的精度,则 LocationManager 会用蜂窝幸好和网络来获取位置。因为这能延长电池寿命,对于 iPhone 来说至关重要。如果你需要更高的精度,LocationManager 会使用 GPS 进行定位,这是非常耗电的。同理,一旦我们收到一个可以接受的位置信息之后,就应当立即停止获取定位信息。
接下来实现委托方法。在 ViewController 的 CLLocationManagerDelegate 扩展中加入代码:
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if locations.count & 0 {
let location = locations.last!
print("Accuracy: \(location.horizontalAccuracy)")
if location.horizontalAccuracy & 100 {
manager.stopUpdatingLocation()
let span = MKCoordinateSpan(latitudeDelta: 0.014, longitudeDelta: 0.014)
let region = MKCoordinateRegion(center: location.coordinate, span: span)
mapView.region = region
代码解释如下:
每当 LocationManager 收到一个位置更新信息,它就会立即通知委托对象,将新的位置信息传递给委托。locations 数组中包含了按时间排序的所有位置,因此最新的位置应当是数组中的最后一个值。首先判断数组中是否包含了至少一个值,并从中获取最新的一个。然后在控制台中打印水平精度。这个值表示了当前位置的位置半径。如果这个值为 50,则说明真实的位置就在这个 50 米半径范围内。
这里判断位置精度是否满足我们的需要。在本例中,100 米就足够了。在真实 App,你可能想要 10 米或者更少的精度,但要达到这种精度很可能要花好几分钟的时间(GPS 定位是耗时的)。
首先停止位置更新以减少电池消耗。然后将 mapView 的中心缩放到这个位置。
在设备上运行 App,注意控制台消息,你会发现不停地接收到位置信息,同时精度会变得越来越高。最后地图中心会缩放到你的当前位置。
注意:除了 horizontalAccuracy 以外还有一个 verticalAccuracy 属性。不同的是这个属性是针对海拔的。如果这个值为 50,表示真实的海拔应该在上下 50 米范围内。这两个属性如果为负,则表明值无效。
添加 Google Places 框架
现在我们有了用户位置,可以来加载 POI 列表了。这需要用到 Google 的 Places API。
Google 的 PLaces API 需要注册才能使用。如果你已经注册过 Google 账号,比如之前为了使用 Maps API 注册的 Google 账号,则只需要在选择 Services。然后跳过后面的几步直到看到 Enabling the Places API。
但是,如果你之前没有用过 Google Places API,你必须 Google 账号。
你可以跳过第二个界面,来到第三个界面,点击 Back to Developer Consoles。
然后点击左上角的 Project/Create Project,输入一个项目名称。要使用 Places API,请找到 Google Places API Web Service 一行并点击链接。接着点击上方的 ENABLE。然后点击 Crendentials 并继续后面几步以获取 API key。
现在,你有了 API key,请打开 PlacesLoader.swift,然后找到 let apiKey = “Your API key” 一行,将其中的内容替换为你的 API key。
这时最好能进行一个测试,但在运行 App 之前,我们需要打开 ViewController.swift 并在 locationManager 属性后面添加两个属性:
fileprivate var startedLoadingPOIs = false
fileprivate var places = [Place]()
startedLoadingPOIs 用于标记请求是否仍然在进行,因为 CLLocationManagerDelegate 方法可能被调用多次,哪怕你停止了位置更新。通过这个标志能避免重复请求。places 属性用于存储收到的 POI。
现在找到 locationManager(manager: didUpdateLocations:) 方法。在 if 语句内,在 “More code later …” 注释之后加入下列代码:
if !startedLoadingPOIs {
startedLoadingPOIs = true
let loader = PlacesLoader()
loader.loadPOIS(location: location, radius: 1000) { placesDict, error in
if let dict = placesDict {
print(dict)
这段代码加载了当前用户位置半径 1000 米范围内的 POI,然后打印到控制台中。
运行 App,查看控制台输出。输出内容类似于如下例子:
"html_attributions" =
"next_page_token" = "CpQCAgEAAJWpTe34EHADqMuEIXEUvbWnzJ3fQ0bs1AlHgK2SdpungTLOeK21xMPoi04rkJrdUUFRtFX1niVKCrz49_MLOFqazbOOV0H7qbrtKCrn61Lgm--DTBc_3Nh9UBeL8h-kDig59HmWwj5N-gPeki8KE4dM6EGMdZsY1xEkt0glaLt9ScuRj_w2G8d2tyKMXtm8oheiGFohz4SnB9d36MgKAjjftQBc31pH1SpnyX2wKVInea7ZvbNFj5I8ooFOatXlp3DD9K6ZaxXdJujXJGzm0pqAsrEyuSg3Dnh3UfXPLdY2gpXBLpHCiMPh90-bzYDMX4SOy2cQOk2FYQVR5UUmLtnrRR9ylIaxQH85RmNmusrtEhDhgRxcCZthJHG4ktJk37sGGhSL3YHgptN2UExsnhzABwmP_6L_mg";
geometry =
location =
lat = "50.5145334";
lng = "8.3931416";
viewport =
northeast =
lat = "50.01";
lng = "8.002";
southwest =
lat = "50.99";
lng = "8.3930603";
icon = "https://maps.gstatic.com/mapfiles/place_api/icons/lodging-71.png";
id = c64c6c1abd02fc4bd504ab6d152a2b;
name = "Schlo\U00df-Hotel Braunfels";
height = 4160;
"html_attributions" =
"&a href=\"https://maps.google.com/maps/contrib//photos\"&Ralph Peters&/a&"
"photo_reference" = "CoQBdwAAABZT7LYlGHmdep61gMOtwpZsYtVeHRWch0PcUZQOuICYHEWnZhKsSkVdMLx3RBTFIz9ymN10osdlqrPcxhxn-vv3iSsg6YyM18A51e3Sy0--jO2u4kCC05zeMyFp-k7C6ygsDsiOK4Dn3gsu_Bf5D-SZt_SrJqkO0Ys6CwTJ75EPEhDcRLUGnYt2tSODqn_XwxKWGhRMrOG9BojlDHFSoktoup1OsbCpkA";
width = 3120;
"place_id" = ChIJdadOzRdPvEcRkItOT1FMzdI;
rating = "3.8";
reference = "CmRSAAAAgvVO1e988IpXI7_u0IsRFCD1U1IUoSXlW7KfXvLb0DDtToodrGbiVtGZApSKAahnClm-_o-Nuixca_azt22lrT6VGwlJ1m6P0s2TqHAEmnD2QasXW6dCaDjKxesXCpLmEhAOanf32ZUsfX7JNLfNuuUXGhRrzQg-vvkQ0pGT-iSOczT5dG_7yg";
scope = GOOGLE;
"point_of_interest",
establishment
vicinity = "Hubertusstra\U00dfe 2, Braunfels";
原谅我糟糕的法语吧 :]
如果返回结果为 NULL,请尝试增加半径值。
现在,我们的 App 获取了用户的位置并加载它附近的 PIO 列表。我们拥有一个用于保存 PIO 的类 Place,虽然我们根本没有用到它。现在我们应该做的就是将 PIO 显示到地图上!
为了在 mapView 上显示标注,我们还需要用另外一个类。依次点击 File\New\File… 菜单,选择 iOS\Swift File 然后点 Next。命名文件为 PlaceAnnotation.swift 然后点击 Create。
编辑 PlaceAnnotation.swift 的代码为:
import Foundation
import MapKit
class PlaceAnnotation: NSObject, MKAnnotation {
let coordinate: CLLocationCoordinate2D
let title: String?
init(location: CLLocationCoordinate2D, title: String) {
self.coordinate = location
self.title = title
super.init()
这里我们让类实现 MKAnnotation 协议并定义了两个属性和一个 init 方法。
接下来应该做的事情就是将 POI 显示到地图上!
回到 ViewController.swift 继续编辑 locationManager(manager: didUpdateLocations:) 方法。找到 print(dict) line 并将它替换为:
guard let placesArray = dict.object(forKey: "results") as? [NSDictionary]
else { return }
for placeDict in placesArray {
let latitude = placeDict.value(forKeyPath: "geometry.location.lat") as! CLLocationDegrees
let longitude = placeDict.value(forKeyPath: "geometry.location.lng") as! CLLocationDegrees
let reference = placeDict.object(forKey: "reference") as! String
let name = placeDict.object(forKey: "name") as! String
let address = placeDict.object(forKey: "vicinity") as! String
let location = CLLocation(latitude: latitude, longitude: longitude)
let place = Place(location: location, reference: reference, name: name, address: address)
self.places.append(place)
let annotation = PlaceAnnotation(location: place.location!.coordinate, title: place.placeName)
DispatchQueue.main.async {
self.mapView.addAnnotation(annotation)
代码解释如下:
guard 语句用于判断返回结果的格式是否正确。
遍历所有 POI。
从 Dictionary 中检索我们需要的数据。返回数据中包含了许多我们并不需要的信息。
用获取的数据创建 Place 对象,然后插入到 places 数组中。
创建 PlaceAnnotation,用于在地图上显示一个标注。
将标注添加到 map view,因为这个操作和 UI 相关,所以需要在主线程中进行。
运行 App。这次,地图上会显示几个大头钉,当你点击其中一个,你会看到地点的名称。这个 App 看起来不错,但说好的增强现实呢?
HDAugmentedReality简介
虽然我们做了不少工作,但我们仍然还有重要的事情没有完成:是时候让增强现实出场了。
在右下角有一颗 Camera 按钮。当我们点击这个按钮,什么也不会发生。在本节,我们会实现这个按钮的动作处理,在摄像头的视野中增加增强现实体验。
使用 HDAugmentedReality 库,能够大大节省我们的时间。这个框架已经包含在我们的开始项目中了。你可以在
中找到它的最新版本,但这个东东是干嘛的?
首先,HDAugmentedReality 能为你在摄像头中增加字幕,以便显示实时视频。
第二,它可以为你添加一个 POI 遮罩层,用于显示它们的位置。
等会你会看到,最后一个功能是我们使用它的最大目的,因为它为我们节省了大量复杂的数学计算。如果你想了解 HDAugmentedReality 背后的数学,请继续。
如果你想立即进入代码,请跳过后面两节,直接阅读“开始编码”一节。
警告,数学来了!
如果你看到这里,表明你想学习 HDAugmentedReality 中的数学。非常好!值得夸奖,但是,这个数学可不是一般的基础数学。在下面的示例中,我们假设有两个给定的点 A 和 B,分别表示地球上的两个坐标。
A 的坐标包含了两个值:经度和纬度。这两个地理学名词用来表示笛卡尔二维坐标系中某个点的 x/y 坐标。
经度表示位于英国格林威治以东或者以西的某个点。这个值在 +180? 到 -180? 之间。
纬度表示位于赤道以南或以北的某个点。这个值在 90?(表示北极) 到 -90? (表示南极)之间,
如果你看一眼标准地球仪,会发现经度线从一极画到另一极——即所谓的子午线。纬度线则是围绕地球平行分布,即所谓的纬圈。你可以翻一下地理课本,两条纬度线大约距离 111 千米,两条子午线之间的距离也是 111 千米。
有 360 条纬度线,每条纬度线表示 360 度中的一度。这样,你可以用以下公式计算地球上任意两点之间的距离:
计算出纬度和经度距离,分别构成直角三角的两条直角边。通过勾股定律,我们可以计算出斜边,即两点间的距离:
很简单,是吧?但不幸的是,这个答案是错误的。
再看一眼地球仪,你会发现,两条纬度线之间的距离总是相等的,但经度线会在两极发生交叉。因此两条相邻经度线之间的距离是变化的,越靠近两级,距离就会越近,当到达极点,距离为 0。也就是说上述公式只在两点位于赤道时才适用。当两点靠两极约近,误差就越大。
为了精确起见,你可以使用“大圆距离”。即两点在球体上的距离,因为地球也是一个球体,准确说接近于球体。这个方法非常实用。已知两点经纬度,计算两点间“大圆距离”的公式为:
这个公式可以计算出两点间的距离,精度为大约 60 千米,对于你想知道东京和纽约之间的距离来说,这已经足够好了。两点间的距离越近,结果越精确。
呃——最困难的工作终于完成了。幸运的是 CLLocation 中有一个 distanceFromLocation: 方法,可以为我们计算出两点间距离。HDAugmentedReality 也使用这个方法。
为什么使用 HDAugmentedReality
你可能会问“切,我还是不明白为什么要使用 HDAugmentedReality ?”确实,创建和显示 frame 并不难,你可以从本站找到有关文章。计算两点间距离也可以调用 CLLocation 的方法实现,没有任何难度。
那为什么我要介绍这个框架?问题在于你必须计算出要在什么地方以及何时显示每个 POI 的覆盖物。假设在设备朝向东北时,有一个 POI 刚好位于你的北方。你应当将 POI 显示在什么地方 —— 中心还是左边?顶端还是下方?
这完全取决于设备在空间中的当前位置。如果设备倾斜向下,你必须将 POI 稍微向上靠一点。如果设备指向南,你根本不能显示这个 POI。这就变得复杂了。
这就是 HDAugmentdReality 最大的功能。它从陀螺仪和指南针读取有用的信息,计算出设备的朝向和倾斜角度。通过这些参数来决定一个 POI 是否需要显示以及显示的位置在哪。
另外,你不需要操心如何显示实时视频并进行复杂和容易出错的数学计算,你只需要将精力集中在如何编写一个让用户乐于使用的 app。
现在,看一眼 HDAugmentedReality\Classes 文件夹中的几个文件:
ARAnnotation: 这个类定义了 POI。
ARAnnotationView: 用于提供 POI 的视图。
ARConfiguration: 提供了几个基本的配置方法和助手方法。
ARTrackingManager: 负责了最“沉重”的工作。幸运的是,这些工作我已经为你做好了。
ARViewController: 为你处理所有“可视对象”的控制器。它显示了一个视频直播界面并将标注放到这个视图上。
创建 AR 视图
打开 ViewController.swift 在 places 属性后定义新属性:
fileprivate var arViewController: ARViewController!
找到 @IBAction func showARController(_ sender: Any) 方法,加入以下代码:
arViewController = ARViewController()
arViewController.dataSource = self
arViewController.maxVisibleAnnotations = 30
arViewController.headingSmoothingFactor = 0.05
arViewController.setAnnotations(places)
self.present(arViewController, animated: true, completion: nil)
设置 arViewController 的数据源。数据源负责提供需要显示的 POI。
修改 arViewController 的属性。maxVisibleAnnotations 定义在同一时刻最多能显示几个标注。为了让 App 能够不卡顿,我们将它设置为30,但如果你位于一个极度活跃的区域时,很可能无法显示全位于你附近的所有 POI。
headingSmoothingFactor 用于将 POI 的视图移动到屏幕上。如果这个值为 1,意味着不使用任何平滑过渡效果,如果你移动你的 iPhone 视图将直从一个地方跳到另一个地方。小于 1 表明这个移动将是动画的,但值越低可能因为动画带来的“滞后感”越强。你可以调整这个值在平滑和速度之间进行取舍。
显示 arViewController。
你可以看一眼 ARViewController.seift 的其他属性,比如 maxDistance,这个属性指定了一个范围,以米为单位,在这个范围中的标注将被显示,而在这个值之外的不被显示。
实现数据源方法
Xcode 在将 dataSource 设置为 self 这行报错,要避免这个错误必须让 ViewController 实现 ARDataSource 协议。这个协议只有一个方法是必须实现的,这个方法需要返回一个 POI 视图。多数情况下你需要提供一个自定义的视图。用 cmd+N 快捷键新建一个文件。选择 iOS\Swift File 并为新文件取名为 AnnotationView.swift。
编辑新文件的代码:
import UIKit
protocol AnnotationViewDelegate {
func didTouch(annotationView: AnnotationView)
class AnnotationView: ARAnnotationView {
var titleLabel: UILabel?
var distanceLabel: UILabel?
var delegate: AnnotationViewDelegate?
override func didMoveToSuperview() {
super.didMoveToSuperview()
func loadUI() {
titleLabel?.removeFromSuperview()
distanceLabel?.removeFromSuperview()
let label = UILabel(frame: CGRect(x: 10, y: 0, width: self.frame.size.width, height: 30))
label.font = UIFont.systemFont(ofSize: 16)
label.numberOfLines = 0
label.backgroundColor = UIColor(white: 0.3, alpha: 0.7)
label.textColor = UIColor.white
self.addSubview(label)
self.titleLabel = label
distanceLabel = UILabel(frame: CGRect(x: 10, y: 30, width: self.frame.size.width, height: 20))
distanceLabel?.backgroundColor = UIColor(white: 0.3, alpha: 0.7)
distanceLabel?.textColor = UIColor.green
distanceLabel?.font = UIFont.systemFont(ofSize: 12)
self.addSubview(distanceLabel!)
if let annotation = annotation as? Place {
titleLabel?.text = annotation.placeName
distanceLabel?.text = String(format: "%.2f km", annotation.distanceFromUser / 1000)
一上来就定义一个委托协议,这个协议会在后面用到。
定义类继承自 ARAnnotaionView,表明这个类将用于作为 POI 的展现视图。
这个视图包含一个用于显示 POI 名字的 Label ,一个用于显示距离的 Label。这些代码声明了两个 Label 属性,第三个属性会在后面用到。
loadUI() 方法用于加载和配置两个 Label。
还需要在这个类中定义两个方法:
override func layoutSubviews() {
super.layoutSubviews()
titleLabel?.frame = CGRect(x: 10, y: 0, width: self.frame.size.width, height: 30)
distanceLabel?.frame = CGRect(x: 10, y: 30, width: self.frame.size.width, height: 20)
override func touchesEnded(_ touches: Set&UITouch&, with event: UIEvent?) {
delegate?.didTouch(annotationView: self)
这个方法在视图重绘时调用,这里你需要正确设置 Label 的位置大小以便重置它们。
这个方法通知委托对象这个视图被点击了,以便委托进行必要的处理。
回到 ViewController.swift,添加一个扩展:
extension ViewController: ARDataSource {
func ar(_ arViewController: ARViewController, viewForAnnotation: ARAnnotation) -& ARAnnotationView {
let annotationView = AnnotationView()
annotationView.annotation = viewForAnnotation
annotationView.delegate = self
annotationView.frame = CGRect(x: 0, y: 0, width: 150, height: 50)
return annotationView
这里,我们创建了一个 AnnotationView 并设置它的委托属性,然后返回它。
在可以测试之前,我们还需要实现另外一个扩展:
extension ViewController: AnnotationViewDelegate {
func didTouch(annotationView: AnnotationView) {
print("Tapped view for POI: \(annotationView.titleLabel?.text)")
在调用相机之前,我们必须在 Info.plist 中添加键值。打开 Info.plist,加入一个键 NSCameraUsageDescription,值设置为 Needed for AR, just like you did for accessing location information。
运行 App,点击 camera 按钮进入 AR 界面。当你第一次这样做的时候,系统会弹出一个请求授权访问摄像头的对话框。点击某个 POI,然后注意看控制台窗口。
现在我们有了一个 AR app,我们能够在摄像头视图上显示 POI 并能够监听到 POI 的点击事件。接下来我们需要定义这个事件的处理逻辑。
打开 ViewController.swift 将 AnnotationViewDelegate 协议扩展修改为:
extension ViewController: AnnotationViewDelegate {
func didTouch(annotationView: AnnotationView) {
if let annotation = annotationView.annotation as? Place {
let placesLoader = PlacesLoader()
placesLoader.loadDetailInformation(forPlace: annotation) { resultDict, error in
if let infoDict = resultDict?.object(forKey: "result") as? NSDictionary {
annotation.phoneNumber = infoDict.object(forKey: "formatted_phone_number") as? String
annotation.website = infoDict.object(forKey: "website") as? String
self.showInfoView(forPlace: annotation)
将 annotationView 的 annotation 转换成 Place 对象。
加载这个 Place 对象附加属性。
对 Place 对象的相关属性进行赋值。
showInfoView 方法会在后面进行实现。
在 showARController(sender:) 后实现方法:
func showInfoView(forPlace place: Place) {
let alert = UIAlertController(title: place.placeName , message: place.infoText, preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: nil))
arViewController.present(alert, animated: true, completion: nil)
为了,我们使用一个 Alert View 来显示 POI 的详细信息,标题显示 POI 的地名,消息则显示 POI 的 infoText。
因为 ViewController 现在不存在于当前视图树中,我们用 arViewController 来显示 alert。
运行 App,查看效果。
最后的完成项目在。
恭喜你,你已经知道如何创建自己的基于定位的 AR app! 另外,你在本教程中也简单了解了 Google Places API。
如果有任何问题或建议,请在下面留言!
没有更多推荐了,}

我要回帖

更多关于 js锚点定位 滚动 导航 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信