【问题标题】:Location Manager delegate call to update TableView issue位置管理器委托调用更新 TableView 问题
【发布时间】:2023-03-22 08:47:02
【问题描述】:

我有一个tableView,其中填充了fetchedResultsController。一切正常。就目前而言,tableView 根据需要从managedObjectContext 加载数据。问题是它的数据太多,所以我创建了一个带有协议的CLLocationManager 类,当它有一个位置时通知tableView,并且tableView 控制器在fetchedResultsController 上添加一个谓词以减少数量可能性。

我遇到的问题是在应用的初始加载上。似乎在 tableView 完成加载之前调用了 LocationManager 委托。

我在首次启动时不断收到此错误,但在后续启动时没有

[error] 错误:严重的应用程序错误。在调用 -controllerDidChangeContent: 期间,从 NSFetchedResultsController 的委托中捕获了一个异常。无效更新:无效的节数。更新后表视图中包含的节数(1)必须等于更新前表视图中包含的节数(0),加上或减去插入或删除的节数(0插入,0已删除)。与 userInfo (null)

CoreData:错误:严重的应用程序错误。在调用 -controllerDidChangeContent: 期间,从 NSFetchedResultsController 的委托中捕获了一个异常。无效更新:无效的节数。更新后表视图中包含的节数(1)必须等于更新前表视图中包含的节数(0),加上或减去插入或删除的节数(0插入,0已删除)。与 userInfo (null)

import CoreLocation

protocol LocationManagerDelegate: class {
  func updateTableView()
}

class LocationManager: NSObject {
  static let sharedInstance = LocationManager()
  public var currentLocation: CLLocation? {
    get {
      return locationManager.location
    }
  }
  private var locationManager = CLLocationManager()
  private override init() {
    super.init()
    locationManager.delegate = self
    self.getCurrentLocation()
  }
  public weak var delegate: LocationManagerDelegate?

  public func updateLocation() {
    getCurrentLocation()
  }

  private func getCurrentLocation() {
    // Also tried without the DispatchQueue
    DispatchQueue.global(qos: .userInitiated).async {
    // DispatchQueue.global(qos: .userInitiated).sync {
      self.locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers
      self.locationManager.requestWhenInUseAuthorization()
      self.locationManager.startUpdatingLocation()
    }
  }
}

extension LocationManager: CLLocationManagerDelegate {
  func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
    delegate?.updateTableView()
    // still crashes
     DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
     self.delegate?.updateTableView()
     }
  }
}

我一直在 AppDelegate.swift 中启动 locationManager 以加快加载时间。

  func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
      LocationManager.sharedInstance.updateLocation()
      return true
  }

认为这是罪魁祸首,我将其移至viewDidLoad 的末尾,但没有成功。该应用程序在初始启动后加载正常,locationManager 的委托,但我无法绕过初始启动时的错误。

这是我的NSFetchedResultsControllerDelegate 实现:

// MARK: - NSFetchedResultsControllerDelegate methods
extension MyViewController: NSFetchedResultsControllerDelegate {
  func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
    tableView.beginUpdates()
  }

  func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
    tableView.endUpdates()
  }

  func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
    switch (type) {
    case .insert:
      if let indexPath = newIndexPath {
        tableView.insertRows(at: [indexPath], with: .automatic)
      }
      break;
    case .delete:
      if let indexPath = indexPath {
        tableView.deleteRows(at: [indexPath], with: .automatic)
      }
      break;
    case .update:
      if let indexPath = indexPath, let cell = tableView.cellForRow(at: indexPath) {
        configureCell(cell, at: indexPath)
      }
      break;
    case .move:
      if let indexPath = indexPath {
        tableView.deleteRows(at: [indexPath], with: .automatic)
      }

      if let newIndexPath = newIndexPath {
        tableView.insertRows(at: [newIndexPath], with: .automatic)
      }
      break;
    }
  }

  func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange sectionInfo: NSFetchedResultsSectionInfo, atSectionIndex sectionIndex: Int, for type: NSFetchedResultsChangeType) {
    print("controller didChange sectionInfo")
  }
}

extension MyViewController: LocationManagerDelegate {
  func updateTableView() {
    filterBasedOnDistance(miles: Double(slider.value))
  }
}

下面是updateTableView()调用的方法:

  private func degreeRangeInMiles(degrees: Double, miles: Double) -> Range<Double> {
    let metersPerDegree: Double = 111_000
    let degreesInMeters = degrees * metersPerDegree

    let metersPerMileDouble = 1_609.344
    let milesDifferential = miles * metersPerMileDouble

    let upperBound = (degreesInMeters + milesDifferential) / metersPerDegree
    let lowerBound = (degreesInMeters - milesDifferential) / metersPerDegree

    return Range(lowerBound..<upperBound)
  }

  func filterBasedOnDistance(miles: Double) {
    guard locationManager.currentLocation != nil else { return }

    let latitude = locationManager.currentLocation?.coordinate.latitude
    let longitude = locationManager.currentLocation?.coordinate.longitude

    let latitudeRange = degreeRangeInMiles(degrees: latitude!, miles: miles)
    let longitudeRange = degreeRangeInMiles(degrees: longitude!, miles: miles)

    let distancePredicate = NSPredicate(format: "latitude BETWEEN { \(latitudeRange.lowerBound),\(latitudeRange.upperBound) } AND longitude BETWEEN {\(longitudeRange.lowerBound),\(longitudeRange.upperBound)}")

    fetchedResultsController.fetchRequest.predicate = distancePredicate

    do {
      try fetchedResultsController.performFetch()
    } catch {
      print(error.localizedDescription)
    }
    tableView.reloadData()
  }

我正在试图找出我的错误在哪里以及如何解决它?感谢您的阅读!

更新

我尝试在我的 LocationManager 类上添加一个面向公众的 get-only 属性以确定设备的授权状态:

  public var locationServicesAuthorized: Bool {
    get {
      return CLLocationManager.locationServicesEnabled()
    }
  }

我更新了我的委托方法调用如下:

extension MyViewController: LocationManagerDelegate {
  func updateTableView() {
    guard locationManager.locationServicesAuthorized else { return }
    filterBasedOnDistance(miles: Double(slider.value))
  }
}

同样的结果。即使我已经实现了beginUpdatesendUpdates,我也得到了无效数量的部分的错误。 ??????????

【问题讨论】:

  • 您的 didChange sectionInfo 方法需要添加/删除部分以响应 .insert 和 .delete 更改类型。
  • @pbasdf 我明白你在说什么。我不知道该放什么。
  • 抱歉 - 我一直在寻找 Swift 中的样板代码,但只能找到 this,它是 Objective C。不过翻译起来并不难。本质上,您只需要检查更改类型并相应地调用 tableView insertSections 或 deleteSections 方法。
  • @pbasdf 如果你想复制我的答案,我会接受你的,所以你得到 25 分。
  • 谢谢,但不用担心 - 你做了腿部工作,我只是指出了方向。

标签: ios uitableview core-data cllocationmanager


【解决方案1】:

pbasdf的建议下,我实现了didChange sectionInfo NSFetchedResultsController 委托方法。 Apple's boilerplate on their site 在 Objective-C 中,但这里是翻译后的 Swift 版本:

  func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange sectionInfo: NSFetchedResultsSectionInfo, atSectionIndex sectionIndex: Int, for type: NSFetchedResultsChangeType) {

    switch type {
    case .insert:
      tableView.insertSections([sectionIndex], with: .fade)
      break
    case .delete:
      tableView.deleteSections([sectionIndex], with: .fade)
    default:
      break
    }
  }

【讨论】:

    猜你喜欢
    • 2013-05-20
    • 1970-01-01
    • 2013-09-08
    • 2013-07-30
    • 1970-01-01
    • 1970-01-01
    • 2017-05-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多