fetchedResultsController
が入力されたtableView
があります。すべてうまく動作します。それはそのままで、tableView
はmanagedObjectContext
のデータを必要に応じてロードします。問題はそれがあまりにも多くのデータですので、私はtableView
に位置を持っていることを通知するプロトコルを持つCLLocationManager
クラスを作成しました。そして、tableViewコントローラは、可能性の数を減らすためにfetchedResultsController
に述語を追加します。Location ManagerがTableViewの問題を更新するための代理コール
私が遭遇した問題は、初期ロードのアプリです。 tableView
の読み込みが完了する前に、LocationManager
代理人が呼び出されているようです。
私はない回目以降の起動に初期起動時にこのエラーを得続ける、しかし:私はロード時間を短縮するための努力にAppDelegate.swift
でlocationManager
を起動していた
[error] error: Serious application error. An exception was caught from the delegate of NSFetchedResultsController during a call to -controllerDidChangeContent:. Invalid update: invalid number of sections. The number of sections contained in the table view after the update (1) must be equal to the number of sections contained in the table view before the update (0), plus or minus the number of sections inserted or deleted (0 inserted, 0 deleted). with userInfo (null)
CoreData: error: Serious application error. An exception was caught from the delegate of NSFetchedResultsController during a call to -controllerDidChangeContent:. Invalid update: invalid number of sections. The number of sections contained in the table view after the update (1) must be equal to the number of sections contained in the table view before the update (0), plus or minus the number of sections inserted or deleted (0 inserted, 0 deleted). with 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()
}
}
}
。
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
クラスのパブリック向きの取得専用のプロパティを追加してみました:
public var locationServicesAuthorized: Bool {
get {
return CLLocationManager.locationServicesEnabled()
}
}
そして、次のように私は私のデリゲートメソッド呼び出しを更新:
extension MyViewController: LocationManagerDelegate {
func updateTableView() {
guard locationManager.locationServicesAuthorized else { return }
filterBasedOnDistance(miles: Double(slider.value))
}
}
同じ結果です。 beginUpdates
とendUpdates
が実装されているにもかかわらず、無効なセクション数があるというエラーが表示されます。
あなたdidChange sectionInfo方法は.insertに応答して.delete変更タイプにセクションを追加/削除する必要があります。 – pbasdf
@pbasdfあなたが話していることが分かります。私はそこに何を入れるべきか分からない。 – Adrian
申し訳ありません - 私はSwiftで定型文を探していましたが、[this](https://developer.apple.com)しか見つけることができません。com/documentation/coredata/nsfetchedresultscontrollerdelegate)。これはObjective Cです。しかし、あまりにも翻訳が難しくありません。基本的にあなただけの変更の種類を確認し、それに応じてのtableViewのinsertSectionsまたはdeleteSectionsメソッドを呼び出す必要があります。 – pbasdf