本篇分享關於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。(應該是吧,心虛)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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的話,就會跳出警告。
以下為程式碼:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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