【问题标题】:significant location change does not trigger on device重大的位置变化不会在设备上触发
【发布时间】:2016-05-29 19:13:03
【问题描述】:

我正在使用重要的位置变化监控。当我在模拟器中运行代码并检查高速公路选项时,我会定期更新位置。我获取这些更新并将它们保存在 NSUserDefaults 中,然后每 10 秒更新一次 tableview,所以我可以查看是否有任何更新。如果我在真实设备上运行该应用程序,我将获得零更新。我把手机放在口袋里,旅行了 80 多公里,去过 2 个城市。零更新。不知道我是不是搞砸了。我附上代码。代码是复制粘贴,随意测试。只需确保在情节提要中使用 TableViewController 并将单元格 ID 设置为 ID。我错过了什么?我正在 iphone 5 上进行测试。

info.plist:

编辑:在苹果文档中找到这个。我的locationManager 应该以不同的方式创建吗?

当应用因位置更新而重新启动时,启动 传递给您的选项字典 应用程序:willFinishLaunchingWithOptions:或 application:didFinishLaunchingWithOptions: 方法包含 UIApplicationLaunchOptionsLocationKey 键。那把钥匙的存在 表示新的位置数据正在等待传送到您的应用程序。 要获取该数据,您必须创建一个新的 CLLocationManager 对象 并重新启动您之前运行的定位服务 应用程序的终止。当您重新启动这些服务时,该位置 manager 将所有待处理的位置更新传递给其委托人。

编辑2:

基于此,位置应至少每 15 分钟更新一次。我的代码中的错误已确认。

如果 GPS 级别的准确性对您的应用来说并不重要并且您不需要 持续跟踪,您可以使用显着变化的位置 服务。使用重大更改位置至关重要 服务正确,因为它至少唤醒了系统和您的应用程序 每 15 分钟一次,即使没有发生位置变化,并且它 持续运行直到你停止它。

edit3:将此代码添加到 AppDelegate didFinishLaunchingWithOptions: 以查看应用程序是否被唤醒。它不会被唤醒 - 我在表视图中看不到 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()
    }
}

【问题讨论】:

  • 您真的看到授权请求了吗?如果没有,您的 plist 中有 NSLocationAlwaysUsageDescription 吗?
  • 是的,请求弹出。是的,NSLocationAlwaysUsageDescription 字符串是在 info.plist 中设置的。
  • 您是否通过双击主页按钮并在应用程序上向上滑动来杀死应用程序?如果您以这种方式杀死一个应用程序,它将不会运行后台重大更改服务。
  • 是的,我正在这样做。我认为大多数用户也经常这样做。有没有更好的暂停应用程序?
  • 不,这不会暂停应用程序,会完全杀死它并阻止进一步的后台交互。无需杀死应用程序,只需轻按一次主页按钮并转到另一个应用程序,而不会杀死另一个应用程序。

标签: ios core-location cllocationmanager


【解决方案1】:

代码有几个问题:

  1. iOS 启动应用时不启动位置更新。
  2. 不请求后台位置更新。
  3. 代码依赖于授权状态的变化来开始位置更新
  4. didChangeAuthorizationStatus 方法中缺少 break 语句。

以下是相关文件:

第一个文档说,在 iOS 启动应用程序时必须完全配置位置管理器对象,以便可以将位置事件传递给应用程序。

重新启动后,您仍必须配置位置管理器对象并调用此方法 [startMonitoringSignificantLocationChanges] 以继续接收位置事件。

我通常这样做是为了确保位置管理器完全启动,无论它是从图标启动还是由 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 中的新功能,如果您想在后台接收位置更新,则必须在每次应用启动时完成。 (就像“我不是在开玩笑,我知道我在后台模式中要求它们,但我真的想要它们”)。

想要在挂起时接收位置更新的应用必须在其应用的 Info.plist 文件中包含 UIBackgroundModes 键(带有位置值),并将此属性的值设置为 YES。

最后你不能依赖 didChangeAuthorizationStatus 被调用。如果您有授权 kCLAuthorizationStatusAuthorizedAlways,那么调用 [locationMgr requestAlwaysAuthorization] 不会导致授权状态发生变化,因此不会调用 didChangeAuthorizationStatus .

如果在调用 requestWhenInUseAuthorization 或 requestAlwaysAuthorization 方法时已知道授权状态,则位置管理器不会向此方法报告当前授权状态。

【讨论】:

  • 没问题 - Apple 已逐渐使 CoreLocation 更难在 iOS 的每个版本中正确使用。我认为它在 iOS6 周围是完美的,现在它只是神秘的。祝你的项目好运。
  • 对此无可争辩。在旁注中。我已经玩了几天了。 Apple 宣称重要位置将至少每 15 分钟触发一次,无论实际位置变化如何。如果我不搬家,我不会得到任何更新。即使您没有移动,您是否每 15 分钟在您的应用中看到一次位置更新?
  • 我只看到它在更换单元格时触发,取出 sim 卡,你永远不会被吵醒。我认为 15 分钟是指手机不会锁定单个单元并停留在那里的事实,它可以在单元之间徘徊,因为移动蜂窝网络在衰落的射频环境中运行。这意味着以前很强的信号可能会随着时间的推移而变弱。手机换塔的速率取决于射频衰落速率和相邻小区使用的功率差阈值参数。
【解决方案2】:

我认为你必须 startUpdatingLocation 无论如何?您是否尝试过插入断点以查看到达的位置?

【讨论】:

  • 如果我将 startUpdatingLocation 设置为 true,我每秒会获得 3 次更新。我认为这仅适用于标准位置服务。我没有,因为它在模拟器上效果很好。
  • 是否可以在后台暂停的应用程序上使用断点?
  • @krompir2 - 不,在这种情况下你不能使用断点。我一般NSLog 消息(所以我可以看控制台)。我还发现将消息记录到一些持久存储中很有用,然后当我将应用程序带到前台时,在表格视图或类似的东西中显示这些保存的消息,这样我就可以诊断应用程序不在时发生的事情前景。
  • 您可以在暂停甚至未运行的进程上设置断点。
  • 如果进程被挂起,在Xcode中点击Debug菜单并选择“Attach to Process”——选择你的进程。您现在已连接,当进程运行时,如果它遇到断点,它将停止。如果进程没有运行,在 Xcode 中单击 Debug 菜单并选择“Attach to Process by PID or Name...”。输入您的进程的名称。单击附加。由于 SLC,调试器将在启动时附加到您的进程。
猜你喜欢
  • 2016-05-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-04-25
  • 1970-01-01
  • 2023-02-05
  • 1970-01-01
相关资源
最近更新 更多