CoreLocation框架详细解析(十) —— 跟踪访问位置简单示例(一)

版本记录

版本号 时间
V1.0 2018.10.13 星期六

前言

很多的app都有定位功能,比如说滴滴,美团等,他们都需要获取客户所在的位置,并且根据位置推送不同的模块数据以及服务,可以说,定位方便了我们的生活,接下来这几篇我们就说一下定位框架CoreLocation。感兴趣的可以看我写的上面几篇。
1. CoreLocation框架详细解析 —— 基本概览(一)
2. CoreLocation框架详细解析 —— 选择定位服务的授权级别(二)
3. CoreLocation框架详细解析 —— 确定定位服务的可用性(三)
4. CoreLocation框架详细解析 —— 获取用户位置(四)
5. CoreLocation框架详细解析 —— 监控用户与地理区域的距离(五)
6. CoreLocation框架详细解析 —— 确定接近iBeacon(六)
7. CoreLocation框架详细解析 —— 将iOS设备转换为iBeacon(七)
8. CoreLocation框架详细解析 —— 获取指向和路线信息(八)
9. CoreLocation框架详细解析 —— 在坐标和用户友好的地名之间转换(九)

开始

首先看一下写作环境

Swift 4.2, iOS 12, Xcode 10

iOS具有非常强大的API,可用于跟踪用户位置和使用地图。您可能正在为当地的咖啡馆制作应用程序,并希望在附近时通知用户特别优惠。或者,你正在制作音乐播放器,并且当用户进入健身房时你想要开始播放一些有弹性的节拍。但是,始终跟踪位置可以非常快地耗尽电池!在这个Core Location教程中,您将学习如何使用框架中一些鲜为人知但功能非常强大的功能。您将学习如何创建一个应用程序,记录白天用户的移动,但也不会耗尽设备的大部分电池。

在本文中,您将制作一个旅行记录应用程序。想象一下,你正在前往一个新的城市。此应用程序不会保留日记,而是会自动跟踪您访问的位置,以便您以后记住它们并将其与您的朋友进行比较。

以下是此应用的功能:

  • 跟踪用户的位置,即使应用程序未打开也是如此。
  • 当应用程序记录新位置时,向用户发送本地通知。
  • 将这些位置保存在文件中。
  • 显示已记录位置的列表。
  • 显示包含用户位置和记录位置的地图。
  • 允许用户手动记录其当前位置。

首先,使用本教程的入门项目。 现在,该应用程序没有做太多。 在第一个选项卡上,您有一个包含位置的空列表。 在第二个选项卡上,您将看到一个空的地图视图。

下面我们就进入正题了。

1. AppDelegate.swift

您在文件顶部还有两个额外的导入:

import CoreLocation
import UserNotifications

CoreLocation框架侦听用户位置更新。 当应用程序记录新位置时,您将使用UserNotifications框架显示横幅通知。

您还可以在类声明中看到两个额外的属性:

let center = UNUserNotificationCenter.current()
let locationManager = CLLocationManager()

通过这两个属性,您将访问上述两个框架的API。

2. PlacesTableViewController.swift

这是应用程序的第一个tab -- UITableViewController子类 - 具有空数据源实现。

3. MapViewController.swift

这是第二个选项卡的视图控制器。 你有从故事板上挂起的MKMapView。 您还可以在导航栏中为加号按钮设置IBAction

4. Location.swift

这是模型类。 它有五个存储的属性:

let latitude: Double
let longitude: Double
let date: Date
let dateString: String
let description: String
  • 纬度和经度是你所期望的,位置的坐标。
  • date日期是记录此位置的确切日期。
  • dateString是日期的人类可读版本。
  • description是该位置的人类可读地址。

还有一个用于将Location对象转换为CLLocationCoordinate2D的计算属性,这在本教程的后面部分将非常有用。

这个类有两个初始化器;您将在本教程后面了解它们。

您需要在磁盘上保存此类的对象。 为了做到这一点,这个类遵循CodableCodable是一种Swift 4语言功能,允许您轻松编码和解码对象。

5. LocationsStorage.swift

最后但并非最不重要的是,使用此单例,您可以将位置保存到应用程序的文档文件夹中。

这个类有几个属性可以从磁盘写入和读取:

private let fileManager: FileManager
private let documentsURL: URL

它还具有访问所有已记录位置的属性,目前,它们在初始化程序中设置为空数组:

private(set) var locations: [Location]

现在你已经开始使用初始代码了,现在是时候添加一些新代码了!


Core Location: Asking for User Locations - 请求用户位置

第一步是请求权限以跟踪用户的位置。 在隐私时代,Apple在保持用户掌控应用可以收集哪些数据方面表现得非常强大。 这就是为什么正确地要求用户允许应用程序访问以收集所需数据非常重要的原因。

1. Providing a Proper Description - 提供正确的描述

要收集位置更改数据,您需要在Info.plist文件中设置两个特殊字符串:

当应用程序请求许可时,应用程序会显示这些字符串。 只要文本符合以下要求,您可以随意将提示更改为您喜欢的任何文本:

  • 鼓励用户为您提供访问权限。
  • 让用户确切知道收集数据的方式和原因。
  • 声明100%正确。

2. Asking for Locations Permissions - 询问地点权限

打开AppDelegate.swift并在application(_:didFinishLaunchingWithOptions:):中的return语句之前添加此行。

locationManager.requestAlwaysAuthorization()

使用此行,您要求用户允许该应用访问后台和前台的位置数据。

Build并运行项目。 您应该得到一个与此类似的对话框,但它将包含您在本教程前面的Info.plist文件中设置的字符串:

点按Always Allow

注意:用户可以限制对后台或后台与前台的位置数据的访问;在这种情况下,开发人员有责任正确地妥善处理这些情况。 为了简化本教程,您将假设用户选择Always Allow

3. Asking for Notifications Permissions - 请求通知权限

位置权限不是您需要的唯一权限:要显示用户通知,您需要获得权限。

为此,您不必指定任何其他字符串。 只需在刚刚添加的行上方添加此代码:

center.requestAuthorization(options: [.alert, .sound]) { granted, error in
}

在这里,您传递options以指定要发布的通知类型。 您还包含一个空闭包,因为在本教程中,您假设用户始终向您授予权限。 您可以在此闭包中处理拒绝。

Build并运行。 你应该看到这个对话框:

点击Allow


Choosing the Most Appropriate Locations Data - 选择最合适的位置数据

Core Location 框架有许多方法来跟踪用户的位置,每个方法都有不同的特征:

  • Standard location services - 标准位置服务:高电池损耗,定位精度高。 非常适合导航或健身应用。
  • Significant location changes - 重大位置变化:中等电池损耗,中等位置精度。 低停止精度。
  • Regional monitoring - 区域监测:低电池电量损耗,很棒的位置精度。 需要特定区域才能进行监控。

这些都不适合您的应用。 低电池影响是必须的 - 否则用户不太可能使用该应用程序。 更重要的是,区域监控也是不受欢迎的,因为您限制用户移动到某些特定区域。

幸运的是,您可以使用另外一个API。

1. Visit Monitoring - 访问监控

访问监控允许您跟踪目的地 - 用户停留一段时间的地方。 每当检测到新的访问时它都会唤醒应用程序,并且非常节能并且不依赖于任何地标。


Subscribe to Location Changes - 订阅位置更改

现在您已经了解了用于获取用户位置的众多核心位置API中的哪一个,现在是时候开始实现它了!

1. CLLocationManager

AppDelegate.swift中,在此行下方:

locationManager.requestAlwaysAuthorization()

添加如下代码

locationManager.startMonitoringVisits()
locationManager.delegate = self

第一行启动监听功能。 Core Location使用代理回调来通知您位置更改。

现在,在文件底部添加此扩展名:

extension AppDelegate: CLLocationManagerDelegate {
  func locationManager(_ manager: CLLocationManager, didVisit visit: CLVisit) {
    // create CLLocation from the coordinates of CLVisit
    let clLocation = CLLocation(latitude: visit.coordinate.latitude, longitude: visit.coordinate.longitude) 

    // Get location description
  }

  func newVisitReceived(_ visit: CLVisit, description: String) {
    let location = Location(visit: visit, descriptionString: description)

    // Save location to disk
  }
}

第一种方法是在记录新访问时从CLLocationManager回调并为您提供CLVisit

CLVisit有四个属性:

  • 1) arrivalDate:visit开始的日期。
  • 2) departureDate:visit结束的日期。
  • 3) coordinate:设备正在visit的区域的中心。
  • 4) horizontalAccuracy:该区域的半径估计值(以米为单位)。

您需要从此数据创建一个Location对象,如果您还记得,有一个初始化程序,它接受CLVisit,日期和描述字符串:

init(_ location: CLLocationCoordinate2D, date: Date, descriptionString: String)

上面唯一缺少的是descriptionString

2. Location Description - 位置说明

要获得描述,您将使用CLGeocoder。 地理编码是将坐标转换为现实世界中的地址或地名的过程。 如果要从一组坐标中获取地址,请使用反向地理编码。 值得庆幸的是,Core Location为我们提供了一个CLGeocoder类,它可以为我们做到这一点!

仍然在AppDelegate.swift中,在类的顶部添加此属性:

static let geoCoder = CLGeocoder()

现在,在locationManager(_:didVisit :)的底部,添加以下代码:

AppDelegate.geoCoder.reverseGeocodeLocation(clLocation) { placemarks, _ in
  if let place = placemarks?.first {
    let description = "\(place)"
    self.newVisitReceived(visit, description: description)
  }
}

在这里,您要求geoCoder从该位置获取地标(placemarks)。 地标包含一系列有关坐标的有用信息,包括它们的地址。 然后,您可以从第一个地标创建描述字符串。 获得描述字符串后,可以调用newVisitReceived(_:description :)

3. Sending Local Notifications - 发送本地通知

现在,是时候在记录新的访问位置时通知用户了。 在newVisitReceived(_:description :)的底部,添加以下内容:

// 1
let content = UNMutableNotificationContent()
content.title = "New Journal entry 📌"
content.body = location.description
content.sound = .default

// 2
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 1, repeats: false)
let request = UNNotificationRequest(identifier: location.dateString, content: content, trigger: trigger)

// 3
center.add(request, withCompletionHandler: nil)

下面详细分说:

  • 1) 创建通知内容。
  • 1) 使用该触发器创建一秒长触发和通知请求。
  • 1) 通过将请求添加到通知中心来安排通知。

Build并运行应用程序。 此时,该应用程序可用,因为它记录访问并通知用户。

如果您使用真实设备并有时间散步,您可以立即测试您的工作。 去某个地方停下来喝杯咖啡。 访问要求您在某个地方停留一段时间。 您应该收到一些通知,如下所示:

正在记录访问,但访问尚未持续。


Faking Data (Optional) - 伪造数据(可选)

散步对你的身体有好处,但是现在在构建这个应用程序的过程中可能会有问题! 要在不实际行走的情况下测试应用程序,您可以使用Route.gpx文件。 这种文件允许您模拟设备或模拟器的GPS位置。 这个特殊的文件将模拟在库比蒂诺的Apple校园周围散步。

要使用它,请在Debug区域中单击Simulate Location图标,然后从列表中选择Route

您可以使用地图或地图应用打开标签,以查看步行路线。

1. Faking CLVisits - 伪造CLVisits

iOS在幕后记录CLVisits,有时您可能需要等待30分钟才能获得回调! 为避免这种情况,您需要实现伪造CLVisit记录的机制。 您将创建CLVisit实例,并且由于CLVisit没有可访问的初始化程序,因此您需要创建一个子类。

将其添加到AppDelegate.swift的末尾:

final class FakeVisit: CLVisit {
  private let myCoordinates: CLLocationCoordinate2D
  private let myArrivalDate: Date
  private let myDepartureDate: Date

  override var coordinate: CLLocationCoordinate2D {
    return myCoordinates
  }
  
  override var arrivalDate: Date {
    return myArrivalDate
  }
  
  override var departureDate: Date {
    return myDepartureDate
  }
  
  init(coordinates: CLLocationCoordinate2D, arrivalDate: Date, departureDate: Date) {
    myCoordinates = coordinates
    myArrivalDate = arrivalDate
    myDepartureDate = departureDate
    super.init()
  }
  
  required init?(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
  }
}

使用此子类,您可以为CLVisit的属性提供初始值。

2. Set Up locationManager - 设置locationManager

现在,您需要locationManager在位置更改时通知您。 为此,在application(_:didFinishLaunchingWithOptions:)末尾,在return语句之前,添加以下内容:

// 1
locationManager.distanceFilter = 35

// 2
locationManager.allowsBackgroundLocationUpdates = true

// 3
locationManager.startUpdatingLocation()

以下是这些行的作用:

  • 1) 当位置更改为n米或更多时,接收位置更新。
  • 2) 允许在后台跟踪位置。
  • 3) 开始监听。

您可以注释掉这3行以关闭访问伪造。

3. Handle Fake Visits - 处理伪造访问

是时候处理位置回调了。 为此,将以下代码添加到AppDelegateCLLocationManagerDelegate扩展:

func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
    // 1
    guard let location = locations.first else {
      return
    }
    
    // 2
    AppDelegate.geoCoder.reverseGeocodeLocation(location) { placemarks, _ in
    if let place = placemarks?.first {
      // 3
      let description = "Fake visit: \(place)"
        
      //4
      let fakeVisit = FakeVisit(
        coordinates: location.coordinate, 
        arrivalDate: Date(), 
        departureDate: Date())
      self.newVisitReceived(fakeVisit, description: description)
    }
  }
}
  • 1) 丢弃除第一个以外的所有位置。
  • 2) 像往常一样抓住位置描述。
  • 3) 将访问标记为伪造。
  • 4) 创建一个FakeVisit实例并将其传递给newVisitReceived函数。

Build并运行应用程序。 打开路线位置模拟。 关闭应用程序或锁定iPhone,您应该每分钟获得一次新通知。

后记

本篇主要讲述了跟踪访问位置,感兴趣的给个赞或者关注~~~

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

推荐阅读更多精彩内容