NSFetchedResultsController inserts the same cell into two sections only when controller is about to insert another section

后端 未结 2 1235
梦谈多话
梦谈多话 2020-12-18 14:43

This is my Message.swift file:

@objc(Message)
class Message: NSManagedObject {

    @NSManaged var content: String
    @NSManaged var createdAt:         


        
相关标签:
2条回答
  • 2020-12-18 15:09

    I do not know why, but to make it working you need to replace:

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

    with

    fetchedResultsController.sections![section].numberOfObjects
    

    For some reason objects!.count returns incorrect number of objects opposite to numberOfObjects property.

    0 讨论(0)
  • 2020-12-18 15:13

    I tried your code, made only one change, this:

    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)
    }
    

    and it works fine, even for 2nd section also!

    For demo purpose, I added ADD button, by pressing it, code will add new message with current date string as content to DB.

    Here is my complete implementation: 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) -> 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()
    }
    }
    

    Here is the iPad screenshot:

    For testing multiple sections, I changed date & time setting os iPad.

    0 讨论(0)
提交回复
热议问题