2016-08-29 1 views
2

Dies ist meine Message.swift Datei einfügen:NSFetchedResultsController fügt die gleiche Zelle in zwei Abschnitte nur dann, wenn Controller über einen anderen Abschnitt

@objc(Message) 
class Message: NSManagedObject { 

    @NSManaged var content: String 
    @NSManaged var createdAt: NSDate 
    @NSManaged var identifier: Int64 

    @NSManaged var conversation: Conversation 

    @NSManaged var sender: Contributor 

    var normalizedCreatedAt: NSDate { 
     return createdAt.dateWithDayMonthAndYearComponents()! 
    } 
} 

Dies ist, wie ich mein Setup 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() 
} 

mit seinen Standarddelegierter.

Auf viewDidLoad hat mein Controller 1 Abschnitt mit 1 Zeile. Ich drucke es auf der Konsole mit der folgenden Funktion:

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 
} 

und der Ausgang ist folgende:

--->>> 
0 
1 

Sobald ich versuche nur ONE eine weitere Nachricht mit unterschiedlichen Abschnitt hinzuzufügen, wird die folgende erhalte ich:

--->>> 
0 
2 
--->>> 
1 
1 

und dann der Fehler:

CoreData: error: Serious application error. An exception was caught from the delegate of NSFetchedResultsController during a call to -controllerDidChangeContent:. Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (2) must be equal to the number of rows contained in that section before the update (1), plus or minus the number of rows inserted or deleted from that section (0 inserted, 0 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out). with userInfo (null)

Warum passiert es so?

NSFetchedResultsController aus irgendeinem Grund lädt die gleiche Zelle in zwei Abschnitte: erste und zweiten. Warum?

HINWEIS:

  • Das Problem entsteht nur, wenn FRC neuen Abschnitt einzufügen. Wenn Sie eine Zeile in den vorhandenen Abschnitt einfügen müssen, gibt es kein Problem. Problem ist stark in Bezug auf Abschnitte.
  • Das Problem besteht nur, wenn FRC versuchen, einen zweiten Abschnitt einzufügen. Wenn es um den dritten oder vierten Abschnitt geht, gibt es überhaupt kein Problem.
+0

können Sie bitte Code für 'numberOfSectionsInTableView' und' cellForRowAtIndexPath' hinzufügen? – D4ttatraya

+0

Ja, ich habe die Frage aktualisiert. –

+0

Code scheint gut, kein mehrdeutiger Ort. überprüfen Sie einfach 'createAt' Eigenschaft fühlt sich richtig und' dateWithDayMonthAndYearComponents() 'Methode funktioniert gut. – D4ttatraya

Antwort

0

Ich weiß nicht, warum, aber es macht das Arbeiten Sie ersetzen müssen:

fetchedResultsController.sections![section].objects!.count 

mit

fetchedResultsController.sections![section].numberOfObjects 

Aus irgendeinem Grund objects!.count kehrt falsche Anzahl der Objekte gegenüber numberOfObjects Eigentum .

1

Ich habe versucht, den Code, machte nur eine Änderung dieses:

var normalizedCreatedAt: String { 
    return getTimeStrWithDayPrecision(createdAt!) 
} 

func getTimeStrWithDayPrecision(date: NSDate) -> String { 
    let formatter = NSDateFormatter() 
    formatter.timeStyle = .NoStyle 
    formatter.dateStyle = .ShortStyle 
    formatter.doesRelativeDateFormatting = true 
    return formatter.stringFromDate(date) 
} 

und es funktioniert gut, auch für auch zweiten Abschnitt!

Für Demo-Zwecke, ich habe ADD Taste hinzugefügt, indem Sie es drücken, Code wird neue Nachricht mit dem aktuellen Datum Zeichenfolge als content an DB hinzufügen.

Hier meine vollständige Implementierung ist: Ansicht 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) -> String { 
let formatter = NSDateFormatter() 
formatter.timeStyle = .NoStyle 
formatter.dateStyle = .ShortStyle 
formatter.doesRelativeDateFormatting = true 
return formatter.stringFromDate(date) 
} 

extension Message { 

@NSManaged var content: String? 
@NSManaged var createdAt: NSDate? 

var normalizedCreatedAt: String { 
    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: String, moc:NSManagedObjectContext) { 
    let entity = NSEntityDescription.entityForName("Message", inManagedObjectContext: moc) 
    super.init(entity: entity!, insertIntoManagedObjectContext: moc) 
    content = text 
    createdAt = NSDate() 
} 
} 

Hier ist das iPad Screenshot:
enter image description here

Für mehrere Abschnitte zu testen, änderte ich Datum & Zeiteinstellung os iPad.

+0

es geht nicht um' String' Ich denke. Ich ersetzt 'normalizedCreatedAt '' 'content' 'zum erstellen von Sektionen und Ausgabe existiert noch. –

+0

Ich habe es auch gleich versucht, funktionierte immer noch gut! – D4ttatraya

+0

Ok, ich versuche es jetzt. –

Verwandte Themen