本篇分享關於iOS開發中,定位功能的一些心得。
版本:swift 4
介面:Xcode 9
希望提示出現的時機有:
- 使用者下載完,第一次打開 app
- 或是在之前已經拒絕被追蹤的情況下,整個app關掉重新打開的時候
- 跳出 alert ,詢問是否接受追蹤他的位置。
- 接受:取得定位,開始提供服務。
- 不接受:跳出 alert 告訴使用者這樣無法得到最好的服務。
- 跳出 alert ,詢問是否接受追蹤他的位置。
這一段的程式碼寫在 AppDelegate.swift 檔案的 didFinishLaunchingWithOptions 區塊內。
這個區塊內的動作,是在使用者下載後第一次打開 app,或是把 app 整個關掉,再重新打開的時候執行。如果只是放到background中,再打開,是不會執行這一段。
所以在這邊會有三種情況,分別是:
- 從未指定權限(下載後初次打開):CLLocationManager.authorizationStatus() == .notDetermine
- 設定為拒絕,或是整機的定位都被關掉:CLLocationManager.authorizationStatus() == .denied || CLLocationManager.authorizationStatus() == .restricted
- 設定為接受:CLLocationManager.authorizationStatus() == .authorizedWhenInUse
在三種情況下,可以再添加你想要執行的內容。
例如在 .notDetermine 的情況下,我們可能會想問他,是否願意接受我們得到你的位置呢?就用 CLLocationManager().requestWhenInUseAuthorization()。
程式碼如下:
*也許你會注意到,這一段程式碼中,我們沒有寫到任何關於跳出警告的部分,事實上這一段我們只要寫在我們的 root.ViewController 裡面就好了,因為無論如何,lunch 之後一定會進入 view。(應該是吧,心虛)
import UIKit | |
import CoreLocation | |
import MapKit | |
//記得要import CoreLocation跟Mapkit | |
@UIApplicationMain | |
class AppDelegate: UIResponder, UIApplicationDelegate, CLLocationManagerDelegate { | |
var window: UIWindow? | |
var myLocationManager : CLLocationManager = CLLocationManager() | |
var myUserDefaults : UserDefaults! | |
let userLocationAuth : String = "locationAuth" | |
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { | |
self.myUserDefaults = UserDefaults.standard | |
myLocationManager.delegate = self | |
myLocationManager.distanceFilter = kCLLocationAccuracyNearestTenMeters | |
myLocationManager.desiredAccuracy = kCLLocationAccuracyBest | |
myLocationManager.activityType = .automotiveNavigation | |
myLocationManager.startUpdatingHeading() | |
if CLLocationManager.authorizationStatus() == .notDetermined{ | |
myLocationManager.requestWhenInUseAuthorization() | |
}//if | |
else if CLLocationManager.authorizationStatus() == .denied || CLLocationManager.authorizationStatus() == .restricted { | |
self.myUserDefaults.set(false, forKey: userLocationAuth) | |
self.myUserDefaults.synchronize() | |
//這邊可以設定你想要執行的功能,例如這邊我想要紀錄使用者的權限狀況,所以用了userDefaults的功能。 | |
//如果你沒有想要執行任何功能,else if 這一段這一段可以完全不寫。 | |
}//else if | |
else if CLLocationManager.authorizationStatus() == .authorizedWhenInUse{ | |
self.myUserDefaults.set(true, forKey: userLocationAuth) | |
self.myUserDefaults.synchronize() | |
//這一段同上,如果你沒有想要執行任何功能,這一段可以不寫 | |
// | |
}//else if | |
return true | |
}//didFinishLaunchingWithOptions | |
}//class | |
//在大引號後面加上它來自哪個開頭是我的習慣,不然我很容易找不到而森77 |
另一種情況是,把 app 放到 background 之後,再開回來,這時候會發生的情況有 :
- 原本就接受定位:直接開始服務。
- 原本接受定位,放到background後,到「設定」中取消:這時候我們會希望跳出 alert 告訴使用者這樣無法取得最好的服務。
- 原本不接受定位,放到background後,到「設定」中改為接受:開始提供服務。
這邊要注意的是:
- 我把 CLLocationManager.authorizationStatus() == .denied ,放在 viewDidLoad 。因為如果你的 app 不只有一個 scene,你把這一段放在 viewDidAppear,每次跳回 root view 的時候,viewDidAppear 都會執行一次,就會再次跳出警告,這樣會讓人覺得很煩。
- CLLocationManager.authorizationStatus() == .authorizedWhenInUse 就可以要放在viewDidAppear裡面,並且後面加上 CLLocationManager().startUpdatingLocation() ,這樣就會在使用者接受的情況下,每次畫面被打開之後就開始記錄使用者定位。
- 這邊再繼續囉說一下關於 CLLocationManager.authorizationStatus() == .authorizedWhenInUse ,我看到有些開發者會放在 ViewDidLoad裡面,這也不是不行,但我有被害妄想症XD,我想說如果使用者一開始拒絕,然後把 app 放到background,再去打開定位權限,想要開始用的時候。這時候viewDidLoad不會被執行,那就會造成誤會了。
- 最後還要加入 func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) ,如果使用者修改了定位權限。例如原本拒絕,後來接受,就要讓 app 開始記錄定位,又或是他在 app lunch 的時候選擇 not allow的話,就會跳出警告。
以下為程式碼:
// | |
// ViewController.swift | |
// myNavegationLayoutAndToolBar | |
// | |
// Created by Chang Shu Hao on 2018/5/5. | |
// Copyright © 2018年 Gary. All rights reserved. | |
// | |
import UIKit | |
import CoreLocation | |
class ViewController: UIViewController, CLLocationManagerDelegate { | |
var myLocationManager : CLLocationManager = CLLocationManager() | |
override func viewDidLoad() { | |
super.viewDidLoad() | |
myLocationManager.delegate = self | |
myLocationManager.distanceFilter = kCLLocationAccuracyNearestTenMeters | |
myLocationManager.desiredAccuracy = kCLLocationAccuracyBest | |
if CLLocationManager.authorizationStatus() == .denied { | |
DispatchQueue.main.async(){ | |
let alertController = UIAlertController(title: "定位權限已關閉", message: "如要變更權限,請至 設定 > 隱私權 > 定位服務 開啟", preferredStyle: .alert) | |
let okAction = UIAlertAction(title: "ok", style: .default, handler: nil) | |
alertController.addAction(okAction) | |
self.present(alertController, animated: true, completion: nil) | |
}//DispatchQueue | |
} | |
}//ViewDidLoad | |
override func viewDidAppear(_ animated: Bool) { | |
super.viewDidAppear(animated) | |
if CLLocationManager.authorizationStatus() == .authorizedWhenInUse{ | |
myLocationManager.startUpdatingLocation() | |
}//else if | |
}//ViewDidAppear | |
override func viewDidDisappear(_ animated: Bool) { | |
super.viewDidDisappear(animated) | |
myLocationManager.stopUpdatingLocation() | |
}//viewDidDisappear | |
override func didReceiveMemoryWarning() { | |
super.didReceiveMemoryWarning() | |
// Dispose of any resources that can be recreated. | |
}//didReceiveMemoryWarning | |
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) { | |
if status == CLAuthorizationStatus.denied{ | |
let alertController = UIAlertController(title: "定位權限已關閉", message: "如要取得咖啡館資訊,請至 設定 > 隱私權 > 定位服務 開啟定位功能", preferredStyle: .alert) | |
let okAction = UIAlertAction(title: "ok", style: .default, handler: nil) | |
alertController.addAction(okAction) | |
self.present(alertController, animated: true, completion: nil) | |
}//if | |
else if status == CLAuthorizationStatus.authorizedWhenInUse{ | |
myLocationManager.startUpdatingLocation() | |
}//else if | |
}//func didChangeAuthorization | |
}//class |
ViewDidLoad做一些基本的設定:
- distanceFileter:官方文件的說明是,你水平移動多少距離會更新一次定位,默認設定是所有移動都會被回報。
- desiredAccuracy:官方文件的說明是,定位的精準度,默認設定是最精準,但有提到越精準需要越多時間以及電力。
ViewDidDisappear:這邊就用 stopUpdatingLocation() 設定離開畫面之後就不再追蹤。
- 不過我覺得有沒有設定好像都沒差,因為上面用的是 requestWhenInUseAuthorization() 詢問,不是always追蹤,所以理當在退出 app 之後就停止追蹤了,設個心安吧 XD 。
重要:執行之前一定要到到Plist 裡面加入 Key: When in Use Usage Description,說明的部分可以留白。
以上是我對於使用者定位權限設定,還有修改設定之後的動作的心得。
若有錯誤的地方,請不吝賜教,有用詞錯誤或不專業的地方也請見諒。
官方說明:
https://developer.apple.com/documentation/corelocation/cllocationmanager/1620562-requestwheninuseauthorization
https://developer.apple.com/library/content/documentation/General/Reference/InfoPlistKeyReference/Articles/CocoaKeys.html