位置管理器委托调用更新TableView问题

时间:2017-10-23 15:57:06

标签: ios uitableview core-data cllocationmanager

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

我遇到的问题是应用的初始加载。似乎在tableView完成加载之前调用了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() } } } 委托。

我在初次发布时遇到此错误,但不会在后续发布中出现

  

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

     

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

locationManager

我一直在AppDelegate.swift启动 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() } } 类上添加面向公众的get-only属性,以确定设备的授权状态:

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

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

beginUpdates

结果相同。即使我已经实现了endUpdates using Android.App; using Android.Content.PM; using Android.Content.Res; using Android.OS; using Android.Support.V4.Widget; using Android.Views; using Android.Widget; using System.Collections; using Android.Support.V7.App; using Android.Support.V4.View; using Android.Support.Design.Widget; using Auth0.OidcClient; using Android.Content; using IdentityModel.OidcClient; using Android.Graphics; using System.Net; using System; using Android.Runtime; using Android.Text.Method; using System.Text; using System.Threading; using System.Collections.Generic; namespace whirlpoolgratitudeapp { [Activity(Label = "whirlpoolgratitudeapp", MainLauncher = true, Icon = "@drawable/icon")] [IntentFilter( new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault, Intent.CategoryBrowsable }, DataScheme = "whirlpoolgratitudeapp.whirlpoolgratitudeapp", DataHost = "lucasmsantos.auth0.com", DataPathPrefix = "/android/whirlpoolgratitudeapp.whirlpoolgratitudeapp/callback")] public class MainActivity : Activity { private ArrayList enderecos; TextView queroreconhecer; TextView crie; TextView conquiste; TextView entregue; TextView viva; TextView comentar; EditText comentário; Spinner spinner; ArrayAdapter adapter; RadioGroup rdgcrie; RadioGroup rdgconquiste; RadioGroup rdgentregue; RadioGroup rdgviva; Button enviar; private Auth0Client client; private AuthorizeState authorizeState; ProgressDialog progress; List<RadioGroup> lista = new List<RadioGroup>(); protected override void OnResume() { base.OnResume(); if (progress != null) { progress.Dismiss(); progress.Dispose(); progress = null; } } protected override async void OnNewIntent(Intent intent) { base.OnNewIntent(intent); var loginResult = await client.ProcessResponseAsync(intent.DataString, authorizeState); var sb = new StringBuilder(); if (loginResult.IsError) { sb.AppendLine($"An error occurred during login: {loginResult.Error}"); } else { sb.AppendLine($"ID Token: {loginResult.IdentityToken}"); sb.AppendLine($"Access Token: {loginResult.AccessToken}"); sb.AppendLine($"Refresh Token: {loginResult.RefreshToken}"); sb.AppendLine(); sb.AppendLine("-- Claims --"); foreach (var claim in loginResult.User.Claims) { sb.AppendLine($"{claim.Type} = {claim.Value}"); } } } private async void LoginButtonOnClick(object sender, EventArgs eventArgs) { progress = new ProgressDialog(this); progress.SetTitle("Log In"); progress.SetMessage("Please wait while redirecting to login screen..."); progress.SetCancelable(false); // disable dismiss by tapping outside of the dialog progress.Show(); // Prepare for the login authorizeState = await client.PrepareLoginAsync(); // Send the user off to the authorization endpoint var uri = Android.Net.Uri.Parse(authorizeState.StartUrl); var intent = new Intent(Intent.ActionView, uri); intent.AddFlags(ActivityFlags.NoHistory); StartActivity(intent); } protected override void OnCreate(Bundle savedInstanceState) { base.OnCreate(savedInstanceState); // Set our view from the "main" layout resource SetContentView(Resource.Layout.Main); client = new Auth0Client(new Auth0ClientOptions { Domain = Resources.GetString(Resource.String.auth0_domain), ClientId = Resources.GetString(Resource.String.auth0_client_id), Activity = this }); //preenche o arraylist com os dados GetEmails(); //cria a instância do spinner declarado no arquivo Main spinner = FindViewById<Spinner>(Resource.Id.spnDados); //cria textview queroreconhecer = FindViewById<TextView>(Resource.Id.txtReconhecer); crie = FindViewById<TextView>(Resource.Id.txtCrie); conquiste = FindViewById<TextView>(Resource.Id.txtConquiste); entregue = FindViewById<TextView>(Resource.Id.txtEntregue); viva = FindViewById<TextView>(Resource.Id.txtViva); comentar = FindViewById<TextView>(Resource.Id.txtComentário); comentário = FindViewById<EditText>(Resource.Id.edtComentario); rdgcrie = FindViewById<RadioGroup>(Resource.Id.rdgCrie); rdgconquiste = FindViewById<RadioGroup>(Resource.Id.rdgConquiste); rdgentregue = FindViewById<RadioGroup>(Resource.Id.rdgEntregue); rdgviva = FindViewById<RadioGroup>(Resource.Id.rdgViva); adapter = new ArrayAdapter(this, Android.Resource.Layout.SimpleListItem1, enderecos); spinner.Adapter = adapter; spinner.ItemSelected += Spinner_ItemSelected; enviar = FindViewById<Button>(Resource.Id.button1); enviar.Click += enviar_Click; lista.Add(rdgconquiste); lista.Add(rdgcrie); lista.Add(rdgentregue); lista.Add(rdgviva); void GetEmails() { enderecos = new ArrayList(); enderecos.Add("Escolha um colaborador"); enderecos.Add("alexandre_bonfim@whirlpool.com"); enderecos.Add("alexandre_t_pires@whirlpool.com"); enderecos.Add("ana_carolina_simoes @whirlpool.com"); enderecos.Add("ana_claudia_s_belarmino@whirlpool.com"); enderecos.Add("andre_costa@whirlpool.com"); enderecos.Add("andre_l_teixeira@whirlpool.com"); enderecos.Add("andreza_a_valle@whirlpool.com"); enderecos.Add("anna_carolina_b_ferreira@whirlpool.com"); enderecos.Add("bruno_b_souza@whirlpool.com"); enderecos.Add("bruno_c_castanho@whirlpool.com"); enderecos.Add("bruno_s_lombardero@whirlpool.com"); enderecos.Add("caio_c_sacoman@whirlpool.com"); enderecos.Add("carla_sedin@whirlpool.com"); enderecos.Add("cassia_r_nascimento@whirlpool.com"); enderecos.Add("celia_r_araujo@whirlpool.com"); enderecos.Add("cesar_leandro_de_oliveira@whirlpool.com"); enderecos.Add("daniel_b_szortyka@whirlpool.com"); enderecos.Add("denis_caciatori@whirlpool.com"); enderecos.Add("elisabete_c_ferreira@whirlpool.com"); enderecos.Add("erick_c_senzaki@whirlpool.com"); enderecos.Add("erika_g_souza@whirlpool.com"); enderecos.Add("fabiana_monteiro@whirlpool.com"); enderecos.Add("fernando_v_santos@whirlpool.com"); enderecos.Add("gabriel_roveda@whirlpool.com"); enderecos.Add("herivelto_alves_jr@whirlpool.com"); enderecos.Add("jefferson_s_pecanha@whirlpool.com"); enderecos.Add("josiane_a_teles@whirlpool.com"); enderecos.Add("juliana_g_saito@whirlpool.com"); enderecos.Add("juliano_ventola@whirlpool.com"); enderecos.Add("leonardo_l_costa@whirlpool.com"); enderecos.Add("leonardo_r_silva@whirlpool.com"); enderecos.Add("lucas_m_santos@whirlpool.com"); enderecos.Add("luiz_perea@whirlpool.com"); enderecos.Add("norma_raphaeli@whirlpool.com"); enderecos.Add("patricia_f_prates@whirlpool.com"); enderecos.Add("priscila_l_dattilo@whirlpool.com"); enderecos.Add("priscila_m_konte@whirlpool.com"); enderecos.Add("reider_a_bernucio@whirlpool.com"); enderecos.Add("renato_occhiuto@whirlpool.com"); enderecos.Add("ricardo_a_fernandes@whirlpool.com"); enderecos.Add("ricardo_matos_campaneruti @whirlpool.com"); enderecos.Add("rogerio_pagotto@whirlpool.com"); enderecos.Add("ruben_c_anacleto@whirlpool.com"); enderecos.Add("taise_azevedo@whirlpool.com"); enderecos.Add("vinicius_marques_assis@whirlpool.com"); enderecos.Add("wanderly_t_limeira@whirlpool.com"); }// fim getEmails void Spinner_ItemSelected(object sender, AdapterView.ItemSelectedEventArgs e) { Spinner spinner = (Spinner)sender; string toast = string.Format("Colaborador selecionado: {0}", spinner.GetItemAtPosition(e.Position)); string welcome = "Bem vindo ao aplicativo de agradecimento"; if (toast.Equals("Escolha um colaborador") ) { Toast.MakeText(this, welcome, ToastLength.Long).Show(); } else { Toast.MakeText(this, toast, ToastLength.Long).Show(); } } void showbox(string msg) { var progressDialog = ProgressDialog.Show(this, "Mensagem", msg, true); new System.Threading.Thread(new ThreadStart(delegate { //LOAD METHOD TO GET ACCOUNT INFO RunOnUiThread(() => Toast.MakeText(this, msg, ToastLength.Long).Show()); //HIDE PROGRESS DIALOG RunOnUiThread(() => progressDialog.Dismiss()); RunOnUiThread(() => progressDialog.Hide()); })).Start(); } void enviar_Click(object sender, EventArgs e) { try { RadioButton rdbgrupo1 = FindViewById<RadioButton>(rdgconquiste.CheckedRadioButtonId); RadioButton rdbgrupo2 = FindViewById<RadioButton>(rdgcrie.CheckedRadioButtonId); RadioButton rdbgrupo3 = FindViewById<RadioButton>(rdgviva.CheckedRadioButtonId); RadioButton rdbgrupo4 = FindViewById<RadioButton>(rdgentregue.CheckedRadioButtonId); int RadioGroupIsChecked(RadioGroup radioGroup) { //-1 means empty selection return radioGroup.CheckedRadioButtonId; } //When user doesn't check a radio button, show a Toast if (RadioGroupIsChecked(rdgconquiste) == -1 & RadioGroupIsChecked(rdgcrie) == -1 & RadioGroupIsChecked(rdgviva) == -1 & RadioGroupIsChecked(rdgentregue) == -1) { string excecao = "Ao menos um botão deve ser selecionado e o comentário deve ser preenchido"; Toast.MakeText(this, excecao, ToastLength.Long).Show(); } else { String emailescolhido = spinner.SelectedItem.ToString(); if (emailescolhido == "Escolha um colaborador abaixo") { string excecao = "Por favor, escolha um colaborador"; Toast.MakeText(this, excecao, ToastLength.Long).Show(); } else { String campocomentario = comentário.Text; string emailchefe = "acursio_maia@whirlpool.com"; var email = new Intent(Android.Content.Intent.ActionSend); //send to email.PutExtra(Android.Content.Intent.ExtraEmail, new string[] { "" + emailescolhido }); //if (emailescolhido == "andreza_a_valle" || emailescolhido.Equals("lucas_m_santos") || emailescolhido == "erika_g_souza@whirlpool.com" || emailescolhido == "caio_c_sacoman") //{ // //cc to // email.PutExtra(Android.Content.Intent.ExtraCc, // new string[] { "comite_clima_ti@whirlpool.com" + emailchefe }); //} //cc to email.PutExtra(Android.Content.Intent.ExtraCc, new string[] { "comite_clima_ti@whirlpool.com" + emailchefe }); //subject email.PutExtra(Android.Content.Intent.ExtraSubject, "SABIA QUE VOCÊ FOI RECONHECIDO?"); //content if (RadioGroupIsChecked(rdgconquiste) != -1 & RadioGroupIsChecked(rdgcrie) != -1 & RadioGroupIsChecked(rdgviva) != -1 & RadioGroupIsChecked(rdgentregue) != -1) { email.PutExtra(Android.Content.Intent.ExtraText, "Você foi reconhecido pelo(s) valor(es) de: " + rdbgrupo1.Text + " , " + rdbgrupo2.Text + " , " + rdbgrupo3.Text + " e " + rdbgrupo4.Text + "" + System.Environment.NewLine + "" + System.Environment.NewLine + campocomentario + System.Environment.NewLine); } else if (RadioGroupIsChecked(rdgconquiste) != -1) { email.PutExtra(Android.Content.Intent.ExtraText, "Você foi reconhecido pelo(s) valor(es) de: " + rdbgrupo1.Text + "" + System.Environment.NewLine + "" + System.Environment.NewLine + campocomentario + System.Environment.NewLine); } else if (RadioGroupIsChecked(rdgcrie) != -1) { email.PutExtra(Android.Content.Intent.ExtraText, "Você foi reconhecido pelo(s) valor(es) de: " + rdbgrupo2.Text + "" + System.Environment.NewLine + "" + System.Environment.NewLine + campocomentario + System.Environment.NewLine); } else if (RadioGroupIsChecked(rdgviva) != -1) { email.PutExtra(Android.Content.Intent.ExtraText, "Você foi reconhecido pelo(s) valor(es) de: " + rdbgrupo3.Text + "" + System.Environment.NewLine + "" + System.Environment.NewLine + campocomentario + System.Environment.NewLine); } else if (RadioGroupIsChecked(rdgentregue) != -1) { email.PutExtra(Android.Content.Intent.ExtraText, "Você foi reconhecido pelo(s) valor(es) de: " + rdbgrupo4.Text + "" + System.Environment.NewLine + "" + System.Environment.NewLine + campocomentario + System.Environment.NewLine); } else if (RadioGroupIsChecked(rdgconquiste) != -1 & RadioGroupIsChecked(rdgcrie) != -1) { email.PutExtra(Android.Content.Intent.ExtraText, "Você foi reconhecido pelo(s) valor(es) de: " + rdbgrupo1.Text + " , " + rdbgrupo2.Text + "" + System.Environment.NewLine + "" + System.Environment.NewLine + campocomentario + System.Environment.NewLine); } else if (RadioGroupIsChecked(rdgconquiste) != -1 & RadioGroupIsChecked(rdgentregue) != -1) { email.PutExtra(Android.Content.Intent.ExtraText, "Você foi reconhecido pelo(s) valor(es) de: " + rdbgrupo1.Text + " , " + rdbgrupo4.Text + "" + System.Environment.NewLine + "" + System.Environment.NewLine + campocomentario + System.Environment.NewLine); } else if (RadioGroupIsChecked(rdgcrie) != -1 & RadioGroupIsChecked(rdgviva) != -1) { email.PutExtra(Android.Content.Intent.ExtraText, "Você foi reconhecido pelo(s) valor(es) de: " + rdbgrupo2.Text + " , " + rdbgrupo3.Text + "" + System.Environment.NewLine + "" + System.Environment.NewLine + campocomentario + System.Environment.NewLine); } else if (RadioGroupIsChecked(rdgcrie) != -1 & RadioGroupIsChecked(rdgentregue) != -1) { email.PutExtra(Android.Content.Intent.ExtraText, "Você foi reconhecido pelo(s) valor(es) de: " + rdbgrupo2.Text + " , " + rdbgrupo4.Text + "" + System.Environment.NewLine + "" + System.Environment.NewLine + campocomentario + System.Environment.NewLine); } else if (RadioGroupIsChecked(rdgviva) != -1 & RadioGroupIsChecked(rdgentregue) != -1) { email.PutExtra(Android.Content.Intent.ExtraText, "Você foi reconhecido pelo(s) valor(es) de: " + rdbgrupo3.Text + " , " + rdbgrupo4.Text + "" + System.Environment.NewLine + "" + System.Environment.NewLine + campocomentario + System.Environment.NewLine); } else if (RadioGroupIsChecked(rdgconquiste) != -1 & RadioGroupIsChecked(rdgcrie) != -1 & RadioGroupIsChecked(rdgviva) != -1) { email.PutExtra(Android.Content.Intent.ExtraText, "Você foi reconhecido pelo(s) valor(es) de: " +rdbgrupo1.Text + " , " + rdbgrupo2.Text + " e " + rdbgrupo3.Text + "" + System.Environment.NewLine + "" + System.Environment.NewLine + campocomentario + System.Environment.NewLine); } else if (RadioGroupIsChecked(rdgconquiste) != -1 & RadioGroupIsChecked(rdgcrie) != -1 & RadioGroupIsChecked(rdgentregue) != -1) { email.PutExtra(Android.Content.Intent.ExtraText, "Você foi reconhecido pelo(s) valor(es) de: " + rdbgrupo1.Text + " , " + rdbgrupo2.Text + " e " + rdbgrupo4.Text + "" + System.Environment.NewLine + "" + System.Environment.NewLine + campocomentario + System.Environment.NewLine); } else if (RadioGroupIsChecked(rdgconquiste) != -1 & RadioGroupIsChecked(rdgviva) != -1 & RadioGroupIsChecked(rdgviva) != -1) { email.PutExtra(Android.Content.Intent.ExtraText, "Você foi reconhecido pelo(s) valor(es) de: " + rdbgrupo1.Text + " , " + rdbgrupo3.Text + " e " + rdbgrupo3.Text + "" + System.Environment.NewLine + "" + System.Environment.NewLine + campocomentario + System.Environment.NewLine); } else if (RadioGroupIsChecked(rdgcrie) != -1 & RadioGroupIsChecked(rdgviva) != -1 & RadioGroupIsChecked(rdgentregue) != -1) { email.PutExtra(Android.Content.Intent.ExtraText, "Você foi reconhecido pelo(s) valor(es) de: " + rdbgrupo2.Text + " , " + rdbgrupo3.Text + " e " + rdbgrupo4.Text + "" + System.Environment.NewLine + "" + System.Environment.NewLine + campocomentario + System.Environment.NewLine); } email.SetType("message/rfc822"); StartActivity(email); Android.App.AlertDialog.Builder alertdialog = new Android.App.AlertDialog.Builder(this); alertdialog.SetTitle("Confirmação de envio"); alertdialog.SetMessage("Email enviado com sucesso"); alertdialog.SetNeutralButton("Ok", delegate { alertdialog.Dispose(); }); alertdialog.Show(); } } } catch (Java.Lang.Exception ex) { showbox(ex.Message); } } } } } ,我也会收到错误信息,即部分数量无效。

1 个答案:

答案 0 :(得分: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
    }
  }