重大位置变化不会在设备上触发
significant location change does not trigger on device
我正在使用重要的位置变化监控。当我 运行 模拟器中的代码并选中高速公路选项时,我会定期更新位置。我获取这些更新并将它们保存在 NSUserDefaults 中,而不是每 10 秒更新一次 tableview,这样我就可以查看是否有任何更新。如果我 运行 真实设备上的应用程序,我得到零更新。我把 phone 放在口袋里,走了 80 多公里,去过 2 个城市。零更新。不确定我是否搞砸了。我附上代码。代码是复制粘贴的,欢迎测试。只需确保在情节提要中使用 TableViewController 并将单元格 ID 设置为 ID。我错过了什么?我正在测试 iphone 5.
info.plist:
编辑:在苹果文档中找到这个。我的 locationManager
应该以不同的方式创建吗?
When an app is relaunched because of a location update, the launch
options dictionary passed to your
application:willFinishLaunchingWithOptions: or
application:didFinishLaunchingWithOptions: method contains the
UIApplicationLaunchOptionsLocationKey key. The presence of that key
signals that new location data is waiting to be delivered to your app.
To obtain that data, you must create a new CLLocationManager object
and restart the location services that you had running prior to your
app’s termination. When you restart those services, the location
manager delivers all pending location updates to its delegate.
编辑2:
基于此,位置应至少每 15 分钟更新一次。我的代码中的错误已确认。
If GPS-level accuracy isn’t critical for your app and you don’t need
continuous tracking, you can use the significant-change location
service. It’s crucial that you use the significant-change location
service correctly, because it wakes the system and your app at least
every 15 minutes, even if no location changes have occurred, and it
runs continuously until you stop it.
edit3:将此代码添加到 AppDelegate didFinishLaunchingWithOptions:
以查看应用程序是否被唤醒。它没有被唤醒 - 我在 table 视图中没有看到 200 200 条目。有点可疑。
if let options = launchOptions {
print("options")
if (launchOptions![UIApplicationLaunchOptionsLocationKey] != nil){
locationManager.startUpdatingLocation()
self.lat.append(Double(200))
self.lon.append(Double(200))
self.times.append(NSDateFormatter.localizedStringFromDate(NSDate(), dateStyle: .NoStyle, timeStyle: .ShortStyle))
NSUserDefaults.standardUserDefaults().setObject(lat, forKey: "lat")
NSUserDefaults.standardUserDefaults().setObject(lon, forKey: "lon")
NSUserDefaults.standardUserDefaults().setObject(times, forKey: "time")
}
代码:
//AppDelegate:
import UIKit
import CoreLocation
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, CLLocationManagerDelegate {
var lat:[CLLocationDegrees]!
var lon:[CLLocationDegrees]!
var times:[String]!
var distances: [String]!
var window: UIWindow?
var locationManager: CLLocationManager!
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
locationManager=CLLocationManager()
locationManager.delegate=self
locationManager.requestAlwaysAuthorization()
let isFirstLaunch = NSUserDefaults.standardUserDefaults().objectForKey("lat")
if isFirstLaunch == nil{
lat = [CLLocationDegrees]()
lon = [CLLocationDegrees]()
times = [String]()
NSUserDefaults.standardUserDefaults().setObject(lat, forKey: "lat")
NSUserDefaults.standardUserDefaults().setObject(lon, forKey: "lon")
NSUserDefaults.standardUserDefaults().setObject(times, forKey: "time")
}else{
lat = NSUserDefaults.standardUserDefaults().arrayForKey("lat") as! [CLLocationDegrees]
lon = NSUserDefaults.standardUserDefaults().arrayForKey("lon") as! [CLLocationDegrees]
times = NSUserDefaults.standardUserDefaults().objectForKey("time") as! [String]
// distances = NSUserDefaults.standardUserDefaults().objectForKey("distance") as! [String]
}
return true
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
print("location updated")
self.lat.append(locations[0].coordinate.latitude)
self.lon.append(locations[0].coordinate.longitude)
self.times.append(NSDateFormatter.localizedStringFromDate(NSDate(), dateStyle: .NoStyle, timeStyle: .ShortStyle))
NSUserDefaults.standardUserDefaults().setObject(lat, forKey: "lat")
NSUserDefaults.standardUserDefaults().setObject(lon, forKey: "lon")
NSUserDefaults.standardUserDefaults().setObject(times, forKey: "time")
print("Location: \(locations[0].coordinate.latitude) \(locations[0].coordinate.longitude)")
}
func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
print("did change AS")
switch status {
case .AuthorizedWhenInUse:
locationManager.startMonitoringSignificantLocationChanges()
case .AuthorizedAlways:
print("to always")
locationManager.startMonitoringSignificantLocationChanges()
if lat.count==0{
self.lat.append((locationManager.location?.coordinate.latitude)!)
self.lon.append((locationManager.location?.coordinate.longitude)!)
self.times.append(NSDateFormatter.localizedStringFromDate(NSDate(), dateStyle: .NoStyle, timeStyle: .ShortStyle))
NSUserDefaults.standardUserDefaults().setObject(lat, forKey: "lat")
NSUserDefaults.standardUserDefaults().setObject(lon, forKey: "lon")
NSUserDefaults.standardUserDefaults().setObject(times, forKey: "time")
}
// locationManager.startUpdatingLocation()
break
default:
locationManager.stopMonitoringSignificantLocationChanges()
break
}
}
}
// 视图控制器
import UIKit
class TableViewController: UITableViewController {
let appDel = UIApplication.sharedApplication().delegate as! AppDelegate
override func viewDidLoad() {
super.viewDidLoad()
NSTimer.scheduledTimerWithTimeInterval(10, target: self, selector: "updateTableView", userInfo: nil, repeats: true)
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return appDel.lon.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("ID", forIndexPath: indexPath)
cell.textLabel?.text = "\(appDel.lat[indexPath.row]) \(appDel.lon[indexPath.row]) \(appDel.times[indexPath.row])"
cell.textLabel?.font = UIFont.systemFontOfSize(9)
return cell
}
func updateTableView(){
self.tableView.reloadData()
}
}
我认为您无论如何都必须启动更新位置?您是否尝试过插入断点以查看到达的位置?
代码有几个问题:
- iOS.
启动应用程序时,位置更新未启动
- 不请求后台位置更新。
- 代码依赖于授权状态的变化来启动位置更新
- didChangeAuthorizationStatus 方法中缺少中断语句。
相关文件如下:
- startMonitoringSignificantLocationChanges
- didChangeAuthorizationStatus
- allowsBackgroundLocationUpdates
第一个文档说当 iOS 启动应用程序时必须完全配置位置管理器对象,以便可以将位置事件传递到应用程序。
Upon relaunch, you must still configure a location manager object and call this method [startMonitoringSignificantLocationChanges] to continue receiving location events.
这就是我通常这样做的方式,以确保位置管理器完全启动,无论它是从图标启动还是通过 iOS(抱歉我使用 ObjectiveC)启动。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
NSLog(@"app launching");
bgTask = UIBackgroundTaskInvalid;
locationMgr = [[CLLocationManager alloc] init];
[locationMgr setDelegate:self];
if([locationMgr respondsToSelector:@selector(setAllowsBackgroundLocationUpdates:)])
[locationMgr setAllowsBackgroundLocationUpdates:YES];
CLAuthorizationStatus authorizationStatus= [CLLocationManager authorizationStatus];
if([launchOptions valueForKey:UIApplicationLaunchOptionsLocationKey] != nil) {
NSLog(@"relaunching because of significant location change - restarting SLC");
[locationMgr startMonitoringSignificantLocationChanges];
}
else
{
if (authorizationStatus == kCLAuthorizationStatusAuthorizedAlways) {
NSLog(@"launching with authorization to always use location - starting SLC");
[locationMgr startMonitoringSignificantLocationChanges];
}
else
{
NSLog(@"launching with no authorization to always use location - requesting authorization");
if([locationMgr respondsToSelector:@selector(requestAlwaysAuthorization)])
[locationMgr requestAlwaysAuthorization];
}
}
return YES;
}
注意对 setAllowsBackgroundLocationUpdates 的调用。这是 iOS9 中的新功能,如果您想在后台接收位置更新,则必须在每次应用程序启动时完成。 (它就像一个"I'm not kidding, I know I asked for them in the background modes, but I really do want them")。
Apps that want to receive location updates when suspended must include the UIBackgroundModes key (with the location value) in their app’s Info.plist file and set the value of this property to YES.
最后你不能依赖 didChangeAuthorizationStatus 被调用。如果您拥有 kCLAuthorizationStatusAuthorizedAlways 授权,则调用 [locationMgr requestAlwaysAuthorization] 不会导致授权状态发生变化,因此 didChangeAuthorizationStatus 未被调用。
If the authorization status is already known when you call the requestWhenInUseAuthorization or requestAlwaysAuthorization method, the location manager does not report the current authorization status to this method.
我正在使用重要的位置变化监控。当我 运行 模拟器中的代码并选中高速公路选项时,我会定期更新位置。我获取这些更新并将它们保存在 NSUserDefaults 中,而不是每 10 秒更新一次 tableview,这样我就可以查看是否有任何更新。如果我 运行 真实设备上的应用程序,我得到零更新。我把 phone 放在口袋里,走了 80 多公里,去过 2 个城市。零更新。不确定我是否搞砸了。我附上代码。代码是复制粘贴的,欢迎测试。只需确保在情节提要中使用 TableViewController 并将单元格 ID 设置为 ID。我错过了什么?我正在测试 iphone 5.
info.plist:
编辑:在苹果文档中找到这个。我的 locationManager
应该以不同的方式创建吗?
When an app is relaunched because of a location update, the launch options dictionary passed to your application:willFinishLaunchingWithOptions: or application:didFinishLaunchingWithOptions: method contains the UIApplicationLaunchOptionsLocationKey key. The presence of that key signals that new location data is waiting to be delivered to your app. To obtain that data, you must create a new CLLocationManager object and restart the location services that you had running prior to your app’s termination. When you restart those services, the location manager delivers all pending location updates to its delegate.
编辑2:
基于此,位置应至少每 15 分钟更新一次。我的代码中的错误已确认。
If GPS-level accuracy isn’t critical for your app and you don’t need continuous tracking, you can use the significant-change location service. It’s crucial that you use the significant-change location service correctly, because it wakes the system and your app at least every 15 minutes, even if no location changes have occurred, and it runs continuously until you stop it.
edit3:将此代码添加到 AppDelegate didFinishLaunchingWithOptions:
以查看应用程序是否被唤醒。它没有被唤醒 - 我在 table 视图中没有看到 200 200 条目。有点可疑。
if let options = launchOptions {
print("options")
if (launchOptions![UIApplicationLaunchOptionsLocationKey] != nil){
locationManager.startUpdatingLocation()
self.lat.append(Double(200))
self.lon.append(Double(200))
self.times.append(NSDateFormatter.localizedStringFromDate(NSDate(), dateStyle: .NoStyle, timeStyle: .ShortStyle))
NSUserDefaults.standardUserDefaults().setObject(lat, forKey: "lat")
NSUserDefaults.standardUserDefaults().setObject(lon, forKey: "lon")
NSUserDefaults.standardUserDefaults().setObject(times, forKey: "time")
}
代码: //AppDelegate:
import UIKit
import CoreLocation
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, CLLocationManagerDelegate {
var lat:[CLLocationDegrees]!
var lon:[CLLocationDegrees]!
var times:[String]!
var distances: [String]!
var window: UIWindow?
var locationManager: CLLocationManager!
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
locationManager=CLLocationManager()
locationManager.delegate=self
locationManager.requestAlwaysAuthorization()
let isFirstLaunch = NSUserDefaults.standardUserDefaults().objectForKey("lat")
if isFirstLaunch == nil{
lat = [CLLocationDegrees]()
lon = [CLLocationDegrees]()
times = [String]()
NSUserDefaults.standardUserDefaults().setObject(lat, forKey: "lat")
NSUserDefaults.standardUserDefaults().setObject(lon, forKey: "lon")
NSUserDefaults.standardUserDefaults().setObject(times, forKey: "time")
}else{
lat = NSUserDefaults.standardUserDefaults().arrayForKey("lat") as! [CLLocationDegrees]
lon = NSUserDefaults.standardUserDefaults().arrayForKey("lon") as! [CLLocationDegrees]
times = NSUserDefaults.standardUserDefaults().objectForKey("time") as! [String]
// distances = NSUserDefaults.standardUserDefaults().objectForKey("distance") as! [String]
}
return true
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
print("location updated")
self.lat.append(locations[0].coordinate.latitude)
self.lon.append(locations[0].coordinate.longitude)
self.times.append(NSDateFormatter.localizedStringFromDate(NSDate(), dateStyle: .NoStyle, timeStyle: .ShortStyle))
NSUserDefaults.standardUserDefaults().setObject(lat, forKey: "lat")
NSUserDefaults.standardUserDefaults().setObject(lon, forKey: "lon")
NSUserDefaults.standardUserDefaults().setObject(times, forKey: "time")
print("Location: \(locations[0].coordinate.latitude) \(locations[0].coordinate.longitude)")
}
func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
print("did change AS")
switch status {
case .AuthorizedWhenInUse:
locationManager.startMonitoringSignificantLocationChanges()
case .AuthorizedAlways:
print("to always")
locationManager.startMonitoringSignificantLocationChanges()
if lat.count==0{
self.lat.append((locationManager.location?.coordinate.latitude)!)
self.lon.append((locationManager.location?.coordinate.longitude)!)
self.times.append(NSDateFormatter.localizedStringFromDate(NSDate(), dateStyle: .NoStyle, timeStyle: .ShortStyle))
NSUserDefaults.standardUserDefaults().setObject(lat, forKey: "lat")
NSUserDefaults.standardUserDefaults().setObject(lon, forKey: "lon")
NSUserDefaults.standardUserDefaults().setObject(times, forKey: "time")
}
// locationManager.startUpdatingLocation()
break
default:
locationManager.stopMonitoringSignificantLocationChanges()
break
}
}
}
// 视图控制器
import UIKit
class TableViewController: UITableViewController {
let appDel = UIApplication.sharedApplication().delegate as! AppDelegate
override func viewDidLoad() {
super.viewDidLoad()
NSTimer.scheduledTimerWithTimeInterval(10, target: self, selector: "updateTableView", userInfo: nil, repeats: true)
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return appDel.lon.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("ID", forIndexPath: indexPath)
cell.textLabel?.text = "\(appDel.lat[indexPath.row]) \(appDel.lon[indexPath.row]) \(appDel.times[indexPath.row])"
cell.textLabel?.font = UIFont.systemFontOfSize(9)
return cell
}
func updateTableView(){
self.tableView.reloadData()
}
}
我认为您无论如何都必须启动更新位置?您是否尝试过插入断点以查看到达的位置?
代码有几个问题:
- iOS. 启动应用程序时,位置更新未启动
- 不请求后台位置更新。
- 代码依赖于授权状态的变化来启动位置更新
- didChangeAuthorizationStatus 方法中缺少中断语句。
相关文件如下:
- startMonitoringSignificantLocationChanges
- didChangeAuthorizationStatus
- allowsBackgroundLocationUpdates
第一个文档说当 iOS 启动应用程序时必须完全配置位置管理器对象,以便可以将位置事件传递到应用程序。
Upon relaunch, you must still configure a location manager object and call this method [startMonitoringSignificantLocationChanges] to continue receiving location events.
这就是我通常这样做的方式,以确保位置管理器完全启动,无论它是从图标启动还是通过 iOS(抱歉我使用 ObjectiveC)启动。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
NSLog(@"app launching");
bgTask = UIBackgroundTaskInvalid;
locationMgr = [[CLLocationManager alloc] init];
[locationMgr setDelegate:self];
if([locationMgr respondsToSelector:@selector(setAllowsBackgroundLocationUpdates:)])
[locationMgr setAllowsBackgroundLocationUpdates:YES];
CLAuthorizationStatus authorizationStatus= [CLLocationManager authorizationStatus];
if([launchOptions valueForKey:UIApplicationLaunchOptionsLocationKey] != nil) {
NSLog(@"relaunching because of significant location change - restarting SLC");
[locationMgr startMonitoringSignificantLocationChanges];
}
else
{
if (authorizationStatus == kCLAuthorizationStatusAuthorizedAlways) {
NSLog(@"launching with authorization to always use location - starting SLC");
[locationMgr startMonitoringSignificantLocationChanges];
}
else
{
NSLog(@"launching with no authorization to always use location - requesting authorization");
if([locationMgr respondsToSelector:@selector(requestAlwaysAuthorization)])
[locationMgr requestAlwaysAuthorization];
}
}
return YES;
}
注意对 setAllowsBackgroundLocationUpdates 的调用。这是 iOS9 中的新功能,如果您想在后台接收位置更新,则必须在每次应用程序启动时完成。 (它就像一个"I'm not kidding, I know I asked for them in the background modes, but I really do want them")。
Apps that want to receive location updates when suspended must include the UIBackgroundModes key (with the location value) in their app’s Info.plist file and set the value of this property to YES.
最后你不能依赖 didChangeAuthorizationStatus 被调用。如果您拥有 kCLAuthorizationStatusAuthorizedAlways 授权,则调用 [locationMgr requestAlwaysAuthorization] 不会导致授权状态发生变化,因此 didChangeAuthorizationStatus 未被调用。
If the authorization status is already known when you call the requestWhenInUseAuthorization or requestAlwaysAuthorization method, the location manager does not report the current authorization status to this method.