C'est mon file Message.swift
:
@objc(Message) class Message: NSManagedObject { @NSManaged var content: Ssortingng @NSManaged var createdAt: NSDate @NSManaged var identifier: Int64 @NSManaged var conversation: Conversation @NSManaged var sender: Consortingbutor var normalizedCreatedAt: NSDate { return createdAt.dateWithDayMonthAndYearComponents()! } }
Voici comment je configure mon FRC:
private func setupFetchedResultsController() { let context = NSManagedObjectContext.MR_defaultContext() let fetchRequest = NSFetchRequest(entityName: "Message") let createdAtDescriptor = NSSortDescriptor(key: "createdAt", ascending: true) fetchRequest.predicate = NSPredicate(format: "conversation.identifier = %lld", conversation.identifier) fetchRequest.sortDescriptors = [createdAtDescriptor] fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: context, sectionNameKeyPath: "normalizedCreatedAt", cacheName: nil) fetchedResultsController.delegate = self try! fetchedResultsController.performFetch() tableView.reloadData() }
avec son délégué standard.
Sur viewDidLoad
mon controller a 1 section avec 1 rangée. Je l'imprime sur la console en utilisant la fonction suivante:
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { print("--->>>") print(section) print(fetchedResultsController.sections![section].objects!.count) return fetchedResultsController.sections![section].objects!.count } func numberOfSectionsInTableView(tableView: UITableView) -> Int { return fetchedResultsController?.sections?.count ?? 0 } func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { let message = fetchedResultsController?.objectAtIndexPath(indexPath) as! Message let cellIdentifier = message.sender.identifier == Settings.currentUser?.profile?.identifier ? SentTableViewCellIdentifier : ReceivedTableViewCellIdentifier let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! TableViewCell cell.cellTitleLabel?.text = message.content return cell }
et la sortie est la suivante:
--->>> 0 1
Une fois que j'essaye d'append juste UN autre message avec une section différente, voici ce que j'ai:
--->>> 0 2 --->>> 1 1
puis l'erreur:
CoreData: erreur: Erreur d'application grave. Une exception a été interceptée par le délégué de NSFetchedResultsController lors d'un appel à -controllerDidChangeContent: Mise à jour invalide: nombre de lignes incorrect dans la section 0. Le nombre de lignes contenues dans une section existante après la mise à jour (2) doit être égal au nombre de lignes contenues dans cette section avant la mise à jour (1), plus ou less de lignes insérées ou supprimées de cette section (0 inséré, 0 supprimé) et plus ou less le nombre de lignes déplacées dans ou hors de cette section (0 déplacé, 0 déplacé). avec userInfo (null)
Pourquoi ça arrive comme ça?
NSFetchedResultsController
pour une raison quelconque, charge la même cellule en deux sections: première et seconde . Pourquoi?
REMARQUE:
J'ai essayé votre code, fait un seul changement, ceci:
var normalizedCreatedAt: Ssortingng { return getTimeStrWithDayPrecision(createdAt!) } func getTimeStrWithDayPrecision(date: NSDate) -> Ssortingng { let formatter = NSDateFormatter() formatter.timeStyle = .NoStyle formatter.dateStyle = .ShortStyle formatter.doesRelativeDateFormatting = true return formatter.ssortingngFromDate(date) }
et ça marche bien, même pour la 2ème section aussi!
À des fins de démonstration, j'ai ajouté le button ADD
, en appuyant dessus, le code appenda un nouveau message avec la string de date actuelle comme content
dans la database.
Voici ma mise en œuvre complète: View controller-
class ChatTableViewController: UITableViewController, NSFetchedResultsControllerDelegate { private var fetchedResultsController: NSFetchedResultsController? private var _mainThreadMOC: NSManagedObjectContext? override func viewDidLoad() { super.viewDidLoad() // Uncomment the following line to preserve selection between presentations // self.clearsSelectionOnViewWillAppear = false // Uncomment the following line to display an Edit button in the navigation bar for this view controller. // self.navigationItem.rightBarButtonItem = self.editButtonItem() setupFetchedResultsController() } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } private func getMainMOC() -> NSManagedObjectContext { if _mainThreadMOC == nil { let appDel = UIApplication.sharedApplication().delegate as! AppDelegate _mainThreadMOC = NSManagedObjectContext(concurrencyType: .MainQueueConcurrencyType) _mainThreadMOC!.persistentStoreCoordinator = appDel.persistentStoreCoordinator _mainThreadMOC!.undoManager = nil } return _mainThreadMOC! } private func setupFetchedResultsController() { let fetchRequest = NSFetchRequest(entityName: "Message") let createdAtDescriptor = NSSortDescriptor(key: "createdAt", ascending: true) fetchRequest.sortDescriptors = [createdAtDescriptor] fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: getMainMOC(), sectionNameKeyPath: "normalizedCreatedAt", cacheName: nil) fetchedResultsController!.delegate = self try! fetchedResultsController!.performFetch() tableView.reloadData() } @IBAction func addMessage(sender: AnyObject) { print("addMessage") let MOC = getMainMOC() let date = NSDate() let _ = Message(text: "\(date)", moc: MOC) do { try MOC.save() }catch { print("Error saving main MOC: \(error)") } } // MARK: - Table view data source override func numberOfSectionsInTableView(tableView: UITableView) -> Int { return fetchedResultsController?.sections?.count ?? 0 } override func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { let sectionInfo = fetchedResultsController!.sections! as [NSFetchedResultsSectionInfo] let title = sectionInfo[section].name let headerHeight:CGFloat = tableView.sectionHeaderHeight let headerLbl = UILabel(frame: CGRectMake(0, 0, tableView.frame.width, headerHeight)) headerLbl.backgroundColor = UIColor.lightGrayColor() headerLbl.textAlignment = .Center headerLbl.text = title return headerLbl } override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { print("--->>>") print(section) print(fetchedResultsController?.sections![section].objects!.count) return (fetchedResultsController?.sections![section].objects!.count)! } override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { let message = fetchedResultsController?.objectAtIndexPath(indexPath) as! Message let cell = tableView.dequeueReusableCellWithIdentifier("MessageCellId", forIndexPath: indexPath) cell.textLabel?.text = message.content! return cell } //MARK: - NSFetchedResultsControllerDelegate func controllerWillChangeContent(controller: NSFetchedResultsController) { tableView.beginUpdates() } func controller(controller: NSFetchedResultsController, didChangeSection sectionInfo: NSFetchedResultsSectionInfo, atIndex sectionIndex: Int, forChangeType type: NSFetchedResultsChangeType) { let indexSet = NSIndexSet(index: sectionIndex) switch type { case .Insert: tableView.insertSections(indexSet, withRowAnimation: .Fade) case .Delete: tableView.deleteSections(indexSet, withRowAnimation: .Fade) case .Update: fallthrough case .Move: tableView.reloadSections(indexSet, withRowAnimation: .Fade) } } func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) { switch type { case .Insert: if let newIndexPath = newIndexPath { tableView.insertRowsAtIndexPaths([newIndexPath], withRowAnimation: .Fade) } case .Delete: if let indexPath = indexPath { tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade) } case .Update: if let indexPath = indexPath { tableView.reloadRowsAtIndexPaths([indexPath], withRowAnimation: .Fade) } case .Move: if let indexPath = indexPath, let newIndexPath = newIndexPath { tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade) tableView.insertRowsAtIndexPaths([newIndexPath], withRowAnimation: .Fade) } } } func controllerDidChangeContent(controller: NSFetchedResultsController) { tableView.endUpdates() } }
Message-
func getTimeStrWithDayPrecision(date: NSDate) -> Ssortingng { let formatter = NSDateFormatter() formatter.timeStyle = .NoStyle formatter.dateStyle = .ShortStyle formatter.doesRelativeDateFormatting = true return formatter.ssortingngFromDate(date) } extension Message { @NSManaged var content: Ssortingng? @NSManaged var createdAt: NSDate? var normalizedCreatedAt: Ssortingng { return getTimeStrWithDayPrecision(createdAt!) } } class Message: NSManagedObject { // Insert code here to add functionality to your managed object subclass override init(entity: NSEntityDescription, insertIntoManagedObjectContext context: NSManagedObjectContext?) { super.init(entity: entity, insertIntoManagedObjectContext: context) } init(text: Ssortingng, moc:NSManagedObjectContext) { let entity = NSEntityDescription.entityForName("Message", inManagedObjectContext: moc) super.init(entity: entity!, insertIntoManagedObjectContext: moc) content = text createdAt = NSDate() } }
Voici la capture d'écran de l'iPad:
Pour tester plusieurs sections, j'ai changé la date et l'heure de réglage de l'iPad.
Je ne sais pas pourquoi, mais pour le faire fonctionner, vous devez replace:
fetchedResultsController.sections![section].objects!.count
avec
fetchedResultsController.sections![section].numberOfObjects
Pour une raison quelconque, les objects!.count
renvoie un nombre incorrect d'objects en face de la propriété numberOfObjects
.