两个 NSFetchResultControllers ,一个 TableViewController
Two NSFetchResultControllers , One TableViewController
我想创建一个包含两个部分的 tableView。每个部分将由不同的 NSFetchResultController 填充。我的数据库中有一个名为 "newsItem" 的实体。在第一部分中,我想显示前五个 newsItems,其中一个名为 "main_article" 的属性等于 true。第二部分将是所有 newsItems.
这是我的 NSFetchResultControllers
private lazy var fetchedResultsController: NSFetchedResultsController? = { [unowned self] in
let fetchRequest = NSFetchRequest(entityName: "NewsItem")
let context = self.managedObjectContext
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "date", ascending: false)]
fetchRequest.predicate = NSPredicate(format: "main_article = YES")
fetchRequest.fetchLimit = 5
let controller = NSFetchedResultsController(fetchRequest: fetchRequest,managedObjectContext: context,sectionNameKeyPath: nil,cacheName: nil)
controller.delegate = self
controller.performFetch(nil)
return controller
}()
private lazy var newsroomFetchedResultsController: NSFetchedResultsController? = { [unowned self] in
let fetchRequest = NSFetchRequest(entityName: "NewsItem")
let context = self.managedObjectContext
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "date", ascending: false)]
fetchRequest.fetchBatchSize = 100
let controller = NSFetchedResultsController(fetchRequest: fetchRequest,managedObjectContext: context,sectionNameKeyPath: nil,cacheName: nil)
controller.delegate = self
controller.performFetch(nil)
return controller
}()
NSFetchResultController 委托方法
func controllerWillChangeContent(controller: NSFetchedResultsController) {
self.tableView.beginUpdates()
}
func controller(controller: NSFetchedResultsController,didChangeObject object: AnyObject,atIndexPath indexPath: NSIndexPath?,forChangeType type: NSFetchedResultsChangeType,
newIndexPath: NSIndexPath?) {
switch type {
case .Insert:
self.tableView.insertRowsAtIndexPaths([newIndexPath!], withRowAnimation: .Automatic)
case .Update:
self.tableView.reloadRowsAtIndexPaths([indexPath!], withRowAnimation: .Automatic)
case .Move:
self.tableView.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: .Automatic)
self.tableView.insertRowsAtIndexPaths([newIndexPath!], withRowAnimation: .Automatic)
case .Delete:
self.tableView.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: .Automatic)
default:
return
}
}
func controllerDidChangeContent(controller: NSFetchedResultsController) {
self.tableView.endUpdates()
}
TableView 委托方法
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 2
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 0 {
return fetchedResultsController?.fetchedObjects?.count ?? 0
}else {
return newsroomFetchedResultsController?.fetchedObjects?.count ?? 0
}
}
func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
if section == 1 {
return (tableView.dequeueReusableCellWithIdentifier("section header") as? UITableViewCell)
}
return nil
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if let newItem = fetchedResultsController?.fetchedObjects?[indexPath.row] as? NewsItem where indexPath.section == 0 {
if let cell = tableView.dequeueReusableCellWithIdentifier("main cell") as? NewMainTableViewCell where indexPath.row == 0{
cell.lblTitle.text = newItem.title
return cell
}
else if let cell = tableView.dequeueReusableCellWithIdentifier("new cell") as? NewTableViewCell{
cell.lblTitle.text = newItem.title
cell.backgroundColor = UIColor.blackColor()
cell.lblTitle.textColor = UIColor.whiteColor()
return cell
}
}
else if let newItem = newsroomFetchedResultsController?.fetchedObjects?[indexPath.row] as? NewsItem where indexPath.section == 1 {
if let cell = tableView.dequeueReusableCellWithIdentifier("new cell") as? NewTableViewCell {
cell.lblTitle.text = newItem.title
cell.backgroundColor = UIColor.whiteColor()
cell.lblTitle.textColor = UIColor.blackColor()
return cell
}
}
return UITableViewCell()
}
func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
if section == 1 {
return 30
}
return 0
}
func tableView(tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return 0.001
}
func tableView(tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
return UIView(frame: CGRectZero)
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
if indexPath.section == 0 && indexPath.row == 0 {
return UIScreen.mainScreen().bounds.width * 1.1
}else {
return 67
}
}
当我尝试 运行 时,出现此错误并且表格视图显示为空:
*2015-06-26 15:33:29.183 Kathimerini[5023:965439] *** 断言失败 -[UITableView _endCellAnimationsWithContext:], /SourceCache/UIKit/UIKit-3347.44/UITableView.m :1623*
2015-06-26 15:33:29.183 Kathimerini[5023:965439] CoreData:错误:严重的应用程序错误。在调用 -controllerDidChangeContent: 期间从 NSFetchedResultsController 的委托捕获了一个异常。更新无效:第 0 节中的行数无效。更新 (5) 后现有节中包含的行数必须等于更新 (5) 前该节中包含的行数,加上或减去行数从该部分插入或删除的行数(1 插入,0 删除)加上或减去移入或移出该部分的行数(0 移入,0 移出)。与用户信息(空)
您的委托方法必须考虑到两个获取的结果控制器。在 insert/delete 等行的委托回调中,您应该首先检查
if controller == self.fetchedResultsController {
// modify section 1
}
else {
// modify section 0
}
您可以通过先而不是设置获取结果控制器的委托来预先测试您的设置是否按预期工作。
此外,请确保您了解索引路径的含义。主要的 FRC 将报告例如要插入到索引路径 (0,10)
的新项目,但您必须将其插入到 (1,10)
。 FRC 不知道您的部分,因为那是您的 table 视图的实现细节。
我想创建一个包含两个部分的 tableView。每个部分将由不同的 NSFetchResultController 填充。我的数据库中有一个名为 "newsItem" 的实体。在第一部分中,我想显示前五个 newsItems,其中一个名为 "main_article" 的属性等于 true。第二部分将是所有 newsItems.
这是我的 NSFetchResultControllers
private lazy var fetchedResultsController: NSFetchedResultsController? = { [unowned self] in
let fetchRequest = NSFetchRequest(entityName: "NewsItem")
let context = self.managedObjectContext
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "date", ascending: false)]
fetchRequest.predicate = NSPredicate(format: "main_article = YES")
fetchRequest.fetchLimit = 5
let controller = NSFetchedResultsController(fetchRequest: fetchRequest,managedObjectContext: context,sectionNameKeyPath: nil,cacheName: nil)
controller.delegate = self
controller.performFetch(nil)
return controller
}()
private lazy var newsroomFetchedResultsController: NSFetchedResultsController? = { [unowned self] in
let fetchRequest = NSFetchRequest(entityName: "NewsItem")
let context = self.managedObjectContext
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "date", ascending: false)]
fetchRequest.fetchBatchSize = 100
let controller = NSFetchedResultsController(fetchRequest: fetchRequest,managedObjectContext: context,sectionNameKeyPath: nil,cacheName: nil)
controller.delegate = self
controller.performFetch(nil)
return controller
}()
NSFetchResultController 委托方法
func controllerWillChangeContent(controller: NSFetchedResultsController) {
self.tableView.beginUpdates()
}
func controller(controller: NSFetchedResultsController,didChangeObject object: AnyObject,atIndexPath indexPath: NSIndexPath?,forChangeType type: NSFetchedResultsChangeType,
newIndexPath: NSIndexPath?) {
switch type {
case .Insert:
self.tableView.insertRowsAtIndexPaths([newIndexPath!], withRowAnimation: .Automatic)
case .Update:
self.tableView.reloadRowsAtIndexPaths([indexPath!], withRowAnimation: .Automatic)
case .Move:
self.tableView.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: .Automatic)
self.tableView.insertRowsAtIndexPaths([newIndexPath!], withRowAnimation: .Automatic)
case .Delete:
self.tableView.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: .Automatic)
default:
return
}
}
func controllerDidChangeContent(controller: NSFetchedResultsController) {
self.tableView.endUpdates()
}
TableView 委托方法
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 2
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 0 {
return fetchedResultsController?.fetchedObjects?.count ?? 0
}else {
return newsroomFetchedResultsController?.fetchedObjects?.count ?? 0
}
}
func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
if section == 1 {
return (tableView.dequeueReusableCellWithIdentifier("section header") as? UITableViewCell)
}
return nil
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if let newItem = fetchedResultsController?.fetchedObjects?[indexPath.row] as? NewsItem where indexPath.section == 0 {
if let cell = tableView.dequeueReusableCellWithIdentifier("main cell") as? NewMainTableViewCell where indexPath.row == 0{
cell.lblTitle.text = newItem.title
return cell
}
else if let cell = tableView.dequeueReusableCellWithIdentifier("new cell") as? NewTableViewCell{
cell.lblTitle.text = newItem.title
cell.backgroundColor = UIColor.blackColor()
cell.lblTitle.textColor = UIColor.whiteColor()
return cell
}
}
else if let newItem = newsroomFetchedResultsController?.fetchedObjects?[indexPath.row] as? NewsItem where indexPath.section == 1 {
if let cell = tableView.dequeueReusableCellWithIdentifier("new cell") as? NewTableViewCell {
cell.lblTitle.text = newItem.title
cell.backgroundColor = UIColor.whiteColor()
cell.lblTitle.textColor = UIColor.blackColor()
return cell
}
}
return UITableViewCell()
}
func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
if section == 1 {
return 30
}
return 0
}
func tableView(tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return 0.001
}
func tableView(tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
return UIView(frame: CGRectZero)
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
if indexPath.section == 0 && indexPath.row == 0 {
return UIScreen.mainScreen().bounds.width * 1.1
}else {
return 67
}
}
当我尝试 运行 时,出现此错误并且表格视图显示为空:
*2015-06-26 15:33:29.183 Kathimerini[5023:965439] *** 断言失败 -[UITableView _endCellAnimationsWithContext:], /SourceCache/UIKit/UIKit-3347.44/UITableView.m :1623*
2015-06-26 15:33:29.183 Kathimerini[5023:965439] CoreData:错误:严重的应用程序错误。在调用 -controllerDidChangeContent: 期间从 NSFetchedResultsController 的委托捕获了一个异常。更新无效:第 0 节中的行数无效。更新 (5) 后现有节中包含的行数必须等于更新 (5) 前该节中包含的行数,加上或减去行数从该部分插入或删除的行数(1 插入,0 删除)加上或减去移入或移出该部分的行数(0 移入,0 移出)。与用户信息(空)
您的委托方法必须考虑到两个获取的结果控制器。在 insert/delete 等行的委托回调中,您应该首先检查
if controller == self.fetchedResultsController {
// modify section 1
}
else {
// modify section 0
}
您可以通过先而不是设置获取结果控制器的委托来预先测试您的设置是否按预期工作。
此外,请确保您了解索引路径的含义。主要的 FRC 将报告例如要插入到索引路径 (0,10)
的新项目,但您必须将其插入到 (1,10)
。 FRC 不知道您的部分,因为那是您的 table 视图的实现细节。