NSFetchedResultsController仅在控制器即将插入另一个部分时才将相同的单元插入两个部分
这是我的Message.swift
文件:
@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()! } }
这就是我设置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() }
与其标准代表。
在viewDidLoad
我的控制器有1个部分,包含1行。 我使用以下function在控制台上打印它:
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 }
输出如下:
--->>> 0 1
一旦我尝试添加另一个不同部分的消息,我得到以下信息:
--->>> 0 2 --->>> 1 1
然后是错误:
CoreData:错误:严重的应用程序错误。 在调用-controllerDidChangeContent:期间,从NSFetchedResultsController的委托中捕获到exception。 无效更新:第0节中的行数无效。更新(2)后现有部分中包含的行数必须等于更新前该部分中包含的行数(1),加上或减去数字从该部分插入或删除的行(0已插入,0已删除)和加或减移入或移出该部分的行数(0移入,0移出)。 with userInfo(null)
为什么会这样?
NSFetchedResultsController
由于某种原因将相同的单元NSFetchedResultsController
载到两个部分: 第一个和第二个部分 。 为什么?
注意:
- 只有当FRC插入新部分时才会出现问题。 如果需要在现有部分中插入行,则没有问题。 问题与部分密切相关。
- 问题仅在于FRC尝试插入SECOND部分时。 当它是第三或第四部分时,根本没有问题。
我尝试了你的代码,只做了一次改动,这个:
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) }
它工作正常,即使是第二节也!
为了演示目的,我添加了ADD
按钮,按下它,代码将添加带有当前日期字符串作为content
新消息到DB。
这是我的完整实现:查看控制器 –
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() } }
信息-
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() } }
这是iPad屏幕截图:
为了测试多个部分,我更改了iPad的日期和时间设置。
我不知道为什么,但要使它工作,你需要更换:
fetchedResultsController.sections![section].objects!.count
同
fetchedResultsController.sections![section].numberOfObjects
由于某种原因, objects!.count
返回与numberOfObjects
属性相反的不正确的对象数。
- 如何符合CBCentralManagerDelegate协议?
- 从nsdictionary中删除键/值
- 在iOS Safari浏览器会话之间不保存Cookie
- 作为函数中的参数使用时,可以将快速闭包设置为默认值吗?
- 我如何在真实设备上testingBraintree + Apple Pay?
- Swift Timer.scheduledTimer()不起作用
- com.apple.product-type.bundle.ui-testing,但是'iphonesimulator'平台没有这样的产品types
- 在列表中重新sortingEKReminder
- iOS / Swift:无法将可选string分配给UILabel文本属性