2016-01-28 17 views
9

Visualizzazione a schermo intero della tabella solo app per iPad. Ho abilitato lo scorrimento per eliminare sulle mie righe. L'animazione della riga termina sempre dopo l'eliminazione (commitEditingStyle completa), ma occasionalmente l'intera vista tabella si blocca. Non nell'intera interfaccia utente, attenzione, quindi non è un thread principale bloccato. Sono in grado di toccare un'intestazione di colonna o toccare il pulsante Indietro sul controller di navigazione, ma il tavolo si blocca e non può essere cancellato. Posso sbloccarlo semplicemente toccando uno dei miei pulsanti di intestazione della colonna.UITableView si blocca dopo lo scorrimento per eliminare, ma non per l'intera interfaccia utente

enter image description here

Sono solo completamente a corto di quello che potrebbe essere la causa del congelamento. Sto usando un NSFetchedResultsController ed ecco il mio codice delegato per quello. E 'abbastanza piastra della caldaia (Aggiornamento: non come piastra della caldaia ora Utilizzando un approccio dosaggio.):

// MARK: NSFetchedResultsController delegate methods 

lazy var deletedSectionIndexes : NSMutableIndexSet = { 
    return NSMutableIndexSet() 
}() 

lazy var insertedSectionIndexes : NSMutableIndexSet = { 
    return NSMutableIndexSet() 
}() 

lazy var deletedRowIndexPaths : [NSIndexPath] = { 
    return [NSIndexPath]() 
}() 

lazy var insertedRowIndexPaths : [NSIndexPath] = { 
    return [NSIndexPath]() 
}() 

lazy var updatedRowIndexPaths : [NSIndexPath] = { 
    return [NSIndexPath]() 
}() 


func controllerWillChangeContent(controller: NSFetchedResultsController) { 

} 

func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) { 
    switch(type) { 

    case .Delete: 
     if let indexPath = indexPath { 
      self.deletedRowIndexPaths.appendDistinct(indexPath) 
     } 
    case .Update: 
     if let indexPath = indexPath { 
      self.updatedRowIndexPaths.appendDistinct(indexPath) 
     } 
    case .Insert: 
     if let newIndexPath = newIndexPath { 
      self.insertedRowIndexPaths.appendDistinct(newIndexPath) 
     } 
    case .Move: 
     if let indexPath = indexPath, newIndexPath = newIndexPath { 
      self.insertedRowIndexPaths.appendDistinct(newIndexPath) 
      self.deletedRowIndexPaths.appendDistinct(indexPath) 
     } 
    } 
} 

func controller(controller: NSFetchedResultsController, didChangeSection sectionInfo: NSFetchedResultsSectionInfo, atIndex sectionIndex: Int, forChangeType type: NSFetchedResultsChangeType) { 
    switch(type) { 

    case .Delete: 
     self.deletedSectionIndexes.addIndex(sectionIndex) 
    case .Insert: 
     self.insertedSectionIndexes.addIndex(sectionIndex) 
    default: 
     break 
    } 
} 

func controllerDidChangeContent(controller: NSFetchedResultsController) { 
    self.tableView.beginUpdates() 
    self.tableView.insertSections(self.insertedSectionIndexes, withRowAnimation: .None) 
    self.tableView.deleteSections(self.deletedSectionIndexes, withRowAnimation: .None) 

    self.tableView.insertRowsAtIndexPaths(self.insertedRowIndexPaths, withRowAnimation: .None) 
    self.tableView.deleteRowsAtIndexPaths(self.deletedRowIndexPaths, withRowAnimation: .None) 
    self.tableView.reloadRowsAtIndexPaths(self.updatedRowIndexPaths, withRowAnimation: .None) 
    self.tableView.endUpdates() 

    self.insertedSectionIndexes.removeAllIndexes() 
    self.deletedSectionIndexes.removeAllIndexes() 
    self.deletedRowIndexPaths.removeAll() 
    self.insertedRowIndexPaths.removeAll() 
    self.updatedRowIndexPaths.removeAll()   
} 

L'eliminazione viene chiamato nel metodo delegato didChangeObject, tuttavia, tecnicamente non è una vera e propria cancellazione. Sto semplicemente impostando una proprietà su -1 e poi salvando quell'elemento attraverso NSMangagedObjectContext - a quel punto NSFRC sembra fare la cosa giusta che è rimuoverlo dalla lista di oggetti recuperati che sono stati recuperati usando questo predicato:

NSPredicate(format: "account = %@ and quantity != -1", account) 

dove account è un oggetto gestito con account valido. La riga scompare senza problemi il 90% o più del tempo. Solo in occasione del completamento dell'animazione il tavolo si congela nel maniero che ho descritto. Non si blocca mai con il pulsante Elimina ancora visualizzato, quindi so che è dopo che commitEditingStyle viene chiamato. Il pulsante Elimina non ha un'implementazione personalizzata. È l'implementazione UITableView predefinita di swipe da eliminare. Qui è il mio metodo commitEditingStyle:

func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) { 
    if editingStyle == .Delete { 
     if let frameboardItem = self.fetchedResultsController.objectAtIndexPath(indexPath) as? IRMFrameBoardItemMO { 
      if frameboardItem.isNew { 
       // If it's never been pushed to the server, just delete locally. This will trigger a table reload 
       // via NSFetchedResultsController 
       DataManager.mainContext.deleteObject(frameboardItem) 
      } else { 
       // Otherwise mark it with a negative quantity which tells the server to delete it and tells the 
       // app to hide it. 
       frameboardItem.quantity = -1 
      } 

      do { 
       try DataManager.mainContext.save() 
      } catch let error as NSError { 
       dLog("Something went wrong: \(error.localizedDescription)") 
      } 

     } 

    } 

} 

Potete vedere un video qui di che cosa sto parlando. Sono passati più di due minuti, quindi potresti non voler vedere tutto, ma lo metto qui per riferimento.

https://vimeo.com/153406113

piacerebbe sentire qualche suggerimento.

Aggiornamento

ho aggiornato i metodi delegato NSFRC utilizzare un approccio dosaggio per assicurare gli aggiornamenti vengono applicati tutti in una volta. Questo non ha risolto il problema. Il tavolo si blocca periodicamente.

+0

Sei sicuro che l'origine dati sia stata aggiornata correttamente? C'è mai una situazione in cui 'frameboardItem' è' nil' nella seguente istruzione: 'if let frameboardItem = self.fetchedResultsController.objectAtIndexPath (indexPath) as? IRMFrameBoardItemMO'? E se è 'nil', la tabella viene aggiornata senza che l'origine dati venga aggiornata? – Alexander

+0

Ho visto il video più volte dove stavi cancellando e si stava congelando. sembra farlo quando si eliminano alcuni in alto e in basso da una sezione, quindi si modificano le sezioni. e cancella in alto e in basso. O forse è solo una o due sezioni particolari. Mi chiedo se c'è un problema nel conteggio delle tue liste o in che modo vengono riposizionati. quello sta tenendo l'interfaccia utente della tabella. Sarebbe bello se fosse possibile ridurlo a come esattamente è riproducibile. Sto solo pensando ad alta voce. –

+0

1) Il TableView è un controller di visualizzazione figlio? 2) Se, a scopo di test, si modifica uno dei pulsanti (che risponde ai tocchi) in modo tale da far scattare il televisore verso l'alto (o verso il basso), in realtà scorre? Ciò potrebbe fornire un indizio sul fatto che la TV stia semplicemente ignorando i tocchi o si sta congelando quando tenta di scorrere. – pbasdf

risposta

2

Ho anche indovinato su questo problema. La mia idea è che controllerDidChangeContent può essere chiamato due o più volte e più veloce degli aggiornamenti delle tabelle e questa causa di più chiamate di tableView.beginUpdates() che può bloccare la tabella.

Quindi, per risolvere questo suggerisco aggiornamento involucro in dispatch_async blocco, o solo semplice flag booleano

func controllerDidChangeContent(controller: NSFetchedResultsController) { 
    dispatch_async(dispatch_get_main_queue(), {() -> Void in 
     self.tableView.beginUpdates() 
     // ..... rest of update code 
     self.updatedRowIndexPaths.removeAll() 
    }) 
} 
+0

Quindi cosa sta facendo esattamente in quel momento? Gli aggiornamenti vengono accodati in quel momento? –

+0

@MattLong sì, gli aggiornamenti vengono eseguiti di conseguenza nella coda – sage444

+0

Sfortunatamente, non è un proiettile d'argento, ma è piuttosto vicino. Ho riprodotto il congelamento anche usando questa tecnica, ma la frequenza è minima - quasi mai. Sto accettando la tua risposta come corretta. Grazie per aver preso una pugnalata al buio. Lo apprezzo. –

0

Utilizzare i blocchi.

Non mi è chiaro da quale thread si accede al MOC, sebbene dal momento che si utilizzi fetchedResultsController, è probabile che sia il principale.

Come tale, si può decidere di eseguire

  • deleteObject
  • save

in un'attesa performBlockAndWait. Questo può aiutare a garantire l'integrità dei dati.Qualcosa sulla falsariga:

DataManager.mainContext.performBlockAndWait {() -> Void in 
    DataManager.mainContext.deleteObject(frameboardItem) 
    if DataManager.mainContext.hasChanges { 
     do { 
      try DataManager.mainContext.save() 
     } catch let error as NSError { 
      dLog("Something went wrong: \(error.localizedDescription)") 
     } 
    } 
} 
0

Non credo che il TableView si blocca a causa di problemi di memoria o iniziare chiamate sbilanciati */endEditing (un'eccezione sarebbe stato gettato o un segnale da inviare).

Penso che potrebbe fare cose su un thread diverso dal thread principale. In tal caso, anche i blocchi non aiuteranno. (Impostare un punto di interruzione e verificare quale thread interrompe ..., anche: test su un dispositivo reale)

La mia idea di risolvere ciò è, provare qualcosa di diverso come aggiungere i dati da aggiungere o rimuovere in un array temporaneo e aggiornare il TableView all'interno di un metodo eseguito (chiamare quel metodo per eseguire esplicitamente il thread principale dopo che il controller dei risultati di recupero ha terminato le chiamate delegate).

0

Hai provato a implementare delegato di NSFetchedResultsControllerDelegate in maniera più comune, intendo iniziare l'aggiornamento tavolo quando fetchedResultController chiede di , fare aggiornamenti e quindi terminare l'aggiornamento?

func controllerWillChangeContent(controller: NSFetchedResultsController) { 
    self.tableView.beginUpdates() 
} 

func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) { 
    /* update table here */ 
} 

func controller(controller: NSFetchedResultsController, didChangeSection sectionInfo: NSFetchedResultsSectionInfo, atIndex sectionIndex: Int, forChangeType type: NSFetchedResultsChangeType) { 
    /* update table here */ 
} 

func controllerDidChangeContent(controller: NSFetchedResultsController) {  
    self.tableView.endUpdates() 
} 

UPADATE:
E 'possibile, che quando si 'contrassegna oggetto come eliminato' si innesca qualche catena più complicato di cambiamento dell'oggetto che a sua volta causare didChangeObject funzione da chiamare per più volte ? Hai rintracciato quante volte è stata richiamata la funzione di ChangeObject durante il singolo "segno di eliminazione"?

+0

Sì. È così che è stato prima di passare all'aggiornamento batch. Vedi la domanda per i dettagli. –

+0

@MattLong yeap, scusami mi sono perso. –