原文:Augmented Reality iOS Tutorial: Location Based
作者:Jean-Pierre Distler

更新说明:本教程由 Jean-Pierre Distler 升级至 Swift3 和 iOS 10。

增强现实是一种很酷的流行技术,你可以通过特定设备(比如iPhone 摄像头或者微软的 Hololens)来观察世界,这个设备会在真实世界的画面上叠加额外的信息。

我猜你也许看过标志物跟踪之类的 App,在这个 App 中,当你将摄像头指向某个标志物时,会立即弹出一个 3D 模型。

在这篇 iOS 增强现实教程中,我们会编写一个 App,利用用户当前位置来找出附近的兴趣点(即 POI)。你可以将这些兴趣点添加到 MapView 上,并在镜头图像中显示它们。

要找出 POI,我们可以使用 Google 的 Places API,然后用 HDAugmentedReality 库将 POI 放到镜头视图中,并计算出用户当前位置到 POI 的距离。

本教程假设你熟悉 MapKit。如果你没用过 MapKit,请先阅读我们的MapKit 教程

开始

请先下载开始项目并自行熟悉其中的内容。在项目导航窗口中选中 Places 项目,在 Target 的 General 窗口中,找到 Siging 栏,将 Team 修改为你自己的开发者账号。然后就可以编译项目了。Main.storyboard 中有一个 Scene 包含了一个 MapView 和一个 UIButton 按钮,它们都已经正确连接了。HDAugmentedReality 库已经导入,另外还有两个 Swift 文件:PlacesLoader.swift 和 Place.swift。这两个类后面我们会用来通过 Google 的 Places API 搜索兴趣点并将搜索结果映射成便于使用的对象。

android增强现实 手机增强现实如何实现_AR

在开始下一步之前,我们首先需要获取用户当前位置。也就是使用 CLLocationManager。打开 ViewController.swift 在 mapView 属性下面添加一个 locationManager 属性。

fileprivate let locationManager = CLLocationManager()

这里我们用一个 CLLocationManager 对象来初始化 locationManager。
为 ViewController 添加下列扩展:

extension ViewController: CLLocationManagerDelegate {
}

在获取用户位置之前必须在 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]) {
  //1
  if locations.count > 0 {
    let location = locations.last!
    print("Accuracy: \(location.horizontalAccuracy)")

    //2
    if location.horizontalAccuracy < 100 {
      //3
      manager.stopUpdatingLocation()
      let span = MKCoordinateSpan(latitudeDelta: 0.014, longitudeDelta: 0.014)
      let region = MKCoordinateRegion(center: location.coordinate, span: span)
      mapView.region = region
      // More code later...
    }
  }
}

代码解释如下:

  1. 每当 LocationManager 收到一个位置更新信息,它就会立即通知委托对象,将新的位置信息传递给委托。locations 数组中包含了按时间排序的所有位置,因此最新的位置应当是数组中的最后一个值。首先判断数组中是否包含了至少一个值,并从中获取最新的一个。然后在控制台中打印水平精度。这个值表示了当前位置的位置半径。如果这个值为 50,则说明真实的位置就在这个 50 米半径范围内。
  2. 这里判断位置精度是否满足我们的需要。在本例中,100 米就足够了。在真实 App,你可能想要 10 米或者更少的精度,但要达到这种精度很可能要花好几分钟的时间(GPS 定位是耗时的)。
  3. 首先停止位置更新以减少电池消耗。然后将 mapView 的中心缩放到这个位置。

在设备上运行 App,注意控制台消息,你会发现不停地接收到位置信息,同时精度会变得越来越高。最后地图中心会缩放到你的当前位置。

android增强现实 手机增强现实如何实现_API_02

注意:除了 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 账号。

android增强现实 手机增强现实如何实现_ios_03

你可以跳过第二个界面,来到第三个界面,点击 Back to Developer Consoles。

android增强现实 手机增强现实如何实现_增强现实_04

然后点击左上角的 Project/Create Project,输入一个项目名称。要使用 Places API,请找到 Google Places API Web Service 一行并点击链接。接着点击上方的 ENABLE。然后点击 Crendentials 并继续后面几步以获取 API key。

加载 POI

现在,你有了 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 …” 注释之后加入下列代码:

//1
if !startedLoadingPOIs {
  startedLoadingPOIs = true
  //2
  let loader = PlacesLoader()
  loader.loadPOIS(location: location, radius: 1000) { placesDict, error in
    //3
    if let dict = placesDict {
      print(dict)
    }
  }
}

这段代码加载了当前用户位置半径 1000 米范围内的 POI,然后打印到控制台中。

运行 App,查看控制台输出。输出内容类似于如下例子:

{
    "html_attributions" =     (
    );
    "next_page_token" = "CpQCAgEAAJWpTe34EHADqMuEIXEUvbWnzJ3fQ0bs1AlHgK2SdpungTLOeK21xMPoi04rkJrdUUFRtFX1niVKCrz49_MLOFqazbOOV0H7qbrtKCrn61Lgm--DTBc_3Nh9UBeL8h-kDig59HmWwj5N-gPeki8KE4dM6EGMdZsY1xEkt0glaLt9ScuRj_w2G8d2tyKMXtm8oheiGFohz4SnB9d36MgKAjjftQBc31pH1SpnyX2wKVInea7ZvbNFj5I8ooFOatXlp3DD9K6ZaxXdJujXJGzm0pqAsrEyuSg3Dnh3UfXPLdY2gpXBLpHCiMPh90-bzYDMX4SOy2cQOk2FYQVR5UUmLtnrRR9ylIaxQH85RmNmusrtEhDhgRxcCZthJHG4ktJk37sGGhSL3YHgptN2UExsnhzABwmP_6L_mg";
    results =     (
                {
            geometry =             {
                location =                 {
                    lat = "50.5145334";
                    lng = "8.3931416";
                };
                viewport =                 {
                    northeast =                     {
                        lat = "50.51476485000001";
                        lng = "8.393168700000002";
                    };
                    southwest =                     {
                        lat = "50.51445624999999";
                        lng = "8.3930603";
                    };
                };
            };
            icon = "https://maps.gstatic.com/mapfiles/place_api/icons/lodging-71.png";
            id = c64c6c1abd02f4764d00a72c4bd504ab6d152a2b;
            name = "Schlo\U00df-Hotel Braunfels";
            photos =             (
                                {
                    height = 4160;
                    "html_attributions" =                     (
                        "<a href=\"https://maps.google.com/maps/contrib/113263673214221090182/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;
            types =             (
                lodging,
                "point_of_interest",
                establishment
            );
            vicinity = "Hubertusstra\U00dfe 2, Braunfels";
        },

原谅我糟糕的法语吧 :]

如果返回结果为 NULL,请尝试增加半径值。

现在,我们的 App 获取了用户的位置并加载它附近的 PIO 列表。我们拥有一个用于保存 PIO 的类 Place,虽然我们根本没有用到它。现在我们应该做的就是将 PIO 显示到地图上!

显示 POI

为了在 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 并将它替换为:

//1
guard let placesArray = dict.object(forKey: "results") as? [NSDictionary]  else { return }
//2
for placeDict in placesArray {
  //3
  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)
  //4
  let place = Place(location: location, reference: reference, name: name, address: address)              
  self.places.append(place)
  //5
  let annotation = PlaceAnnotation(location: place.location!.coordinate, title: place.placeName)
  //6
  DispatchQueue.main.async {
    self.mapView.addAnnotation(annotation)
  }
}

代码解释如下:

  1. guard 语句用于判断返回结果的格式是否正确。
  2. 遍历所有 POI。
  3. 从 Dictionary 中检索我们需要的数据。返回数据中包含了许多我们并不需要的信息。
  4. 用获取的数据创建 Place 对象,然后插入到 places 数组中。
  5. 创建 PlaceAnnotation,用于在地图上显示一个标注。
  6. 将标注添加到 map view,因为这个操作和 UI 相关,所以需要在主线程中进行。

运行 App。这次,地图上会显示几个大头钉,当你点击其中一个,你会看到地点的名称。这个 App 看起来不错,但说好的增强现实呢?

android增强现实 手机增强现实如何实现_增强现实_05

HDAugmentedReality简介

虽然我们做了不少工作,但我们仍然还有重要的事情没有完成:是时候让增强现实出场了。

在右下角有一颗 Camera 按钮。当我们点击这个按钮,什么也不会发生。在本节,我们会实现这个按钮的动作处理,在摄像头的视野中增加增强现实体验。
使用 HDAugmentedReality 库,能够大大节省我们的时间。这个框架已经包含在我们的开始项目中了。你可以在 Github 中找到它的最新版本,但这个东东是干嘛的?

首先,HDAugmentedReality 能为你在摄像头中增加字幕,以便显示实时视频。
第二,它可以为你添加一个 POI 遮罩层,用于显示它们的位置。

等会你会看到,最后一个功能是我们使用它的最大目的,因为它为我们节省了大量复杂的数学计算。如果你想了解 HDAugmentedReality 背后的数学,请继续。

如果你想立即进入代码,请跳过后面两节,直接阅读“开始编码”一节。

警告,数学来了!

如果你看到这里,表明你想学习 HDAugmentedReality 中的数学。非常好!值得夸奖,但是,这个数学可不是一般的基础数学。在下面的示例中,我们假设有两个给定的点 A 和 B,分别表示地球上的两个坐标。

A 的坐标包含了两个值:经度和纬度。这两个地理学名词用来表示笛卡尔二维坐标系中某个点的 x/y 坐标。

  • 经度表示位于英国格林威治以东或者以西的某个点。这个值在 +180º 到 -180º 之间。
  • 纬度表示位于赤道以南或以北的某个点。这个值在 90º(表示北极) 到 -90º (表示南极)之间,

如果你看一眼标准地球仪,会发现经度线从一极画到另一极——即所谓的子午线。纬度线则是围绕地球平行分布,即所谓的纬圈。你可以翻一下地理课本,两条纬度线大约距离 111 千米,两条子午线之间的距离也是 111 千米。

有 360 条纬度线,每条纬度线表示 360 度中的一度。这样,你可以用以下公式计算地球上任意两点之间的距离:

android增强现实 手机增强现实如何实现_android增强现实_06


android增强现实 手机增强现实如何实现_android增强现实_06

计算出纬度和经度距离,分别构成直角三角的两条直角边。通过勾股定律,我们可以计算出斜边,即两点间的距离:

android增强现实 手机增强现实如何实现_API_08

很简单,是吧?但不幸的是,这个答案是错误的。

再看一眼地球仪,你会发现,两条纬度线之间的距离总是相等的,但经度线会在两极发生交叉。因此两条相邻经度线之间的距离是变化的,越靠近两级,距离就会越近,当到达极点,距离为 0。也就是说上述公式只在两点位于赤道时才适用。当两点靠两极约近,误差就越大。

为了精确起见,你可以使用“大圆距离”。即两点在球体上的距离,因为地球也是一个球体,准确说接近于球体。这个方法非常实用。已知两点经纬度,计算两点间“大圆距离”的公式为:

android增强现实 手机增强现实如何实现_ios_09

这个公式可以计算出两点间的距离,精度为大约 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()
//1
arViewController.dataSource = self
//2
arViewController.maxVisibleAnnotations = 30
//3
arViewController.headingSmoothingFactor = 0.05

arViewController.setAnnotations(places)
// 4
self.present(arViewController, animated: true, completion: nil)
  1. 设置 arViewController 的数据源。数据源负责提供需要显示的 POI。
  2. 修改 arViewController 的属性。maxVisibleAnnotations 定义在同一时刻最多能显示几个标注。为了让 App 能够不卡顿,我们将它设置为30,但如果你位于一个极度活跃的区域时,很可能无法显示全位于你附近的所有 POI。
  3. headingSmoothingFactor 用于将 POI 的视图移动到屏幕上。如果这个值为 1,意味着不使用任何平滑过渡效果,如果你移动你的 iPhone 视图将直从一个地方跳到另一个地方。小于 1 表明这个移动将是动画的,但值越低可能因为动画带来的“滞后感”越强。你可以调整这个值在平滑和速度之间进行取舍。
  4. 显示 arViewController。

你可以看一眼 ARViewController.seift 的其他属性,比如 maxDistance,这个属性指定了一个范围,以米为单位,在这个范围中的标注将被显示,而在这个值之外的不被显示。

实现数据源方法

Xcode 在将 dataSource 设置为 self 这行报错,要避免这个错误必须让 ViewController 实现 ARDataSource 协议。这个协议只有一个方法是必须实现的,这个方法需要返回一个 POI 视图。多数情况下你需要提供一个自定义的视图。用 cmd+N 快捷键新建一个文件。选择 iOS\Swift File 并为新文件取名为 AnnotationView.swift。

编辑新文件的代码:

import UIKit

//1
protocol AnnotationViewDelegate {
  func didTouch(annotationView: AnnotationView)
}

//2
class AnnotationView: ARAnnotationView {
  //3
  var titleLabel: UILabel?
  var distanceLabel: UILabel?
  var delegate: AnnotationViewDelegate?

  override func didMoveToSuperview() {
    super.didMoveToSuperview()

    loadUI()
  }

  //4
  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)
    }
  }
}
  1. 一上来就定义一个委托协议,这个协议会在后面用到。
  2. 定义类继承自 ARAnnotaionView,表明这个类将用于作为 POI 的展现视图。
  3. 这个视图包含一个用于显示 POI 名字的 Label ,一个用于显示距离的 Label。这些代码声明了两个 Label 属性,第三个属性会在后面用到。
  4. loadUI() 方法用于加载和配置两个 Label。

还需要在这个类中定义两个方法:

//1
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)
}

//2
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
  delegate?.didTouch(annotationView: self)
}
  1. 这个方法在视图重绘时调用,这里你需要正确设置 Label 的位置大小以便重置它们。
  2. 这个方法通知委托对象这个视图被点击了,以便委托进行必要的处理。

回到 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,然后注意看控制台窗口。

android增强现实 手机增强现实如何实现_AR_10

收尾工作

现在我们有了一个 AR app,我们能够在摄像头视图上显示 POI 并能够监听到 POI 的点击事件。接下来我们需要定义这个事件的处理逻辑。

打开 ViewController.swift 将 AnnotationViewDelegate 协议扩展修改为:

extension ViewController: AnnotationViewDelegate {
  func didTouch(annotationView: AnnotationView) {
  //1
    if let annotation = annotationView.annotation as? Place {
    //2
      let placesLoader = PlacesLoader()
      placesLoader.loadDetailInformation(forPlace: annotation) { resultDict, error in

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

          //4
          self.showInfoView(forPlace: annotation)
        }
      }
    }
  }
}
  1. 将 annotationView 的 annotation 转换成 Place 对象。
  2. 加载这个 Place 对象附加属性。
  3. 对 Place 对象的相关属性进行赋值。
  4. showInfoView 方法会在后面进行实现。

在 showARController(sender:) 后实现方法:

func showInfoView(forPlace place: Place) {
  //1
  let alert = UIAlertController(title: place.placeName , message: place.infoText, preferredStyle: UIAlertControllerStyle.alert)
  alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: nil))
   //2 
  arViewController.present(alert, animated: true, completion: nil)
}
  1. 为了,我们使用一个 Alert View 来显示 POI 的详细信息,标题显示 POI 的地名,消息则显示 POI 的 infoText。
  2. 因为 ViewController 现在不存在于当前视图树中,我们用 arViewController 来显示 alert。

运行 App,查看效果。

android增强现实 手机增强现实如何实现_android增强现实_11

结束

最后的完成项目在这里下载

恭喜你,你已经知道如何创建自己的基于定位的 AR app! 另外,你在本教程中也简单了解了 Google Places API。

如果有任何问题或建议,请在下面留言!