Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save fabb/f280521420a421af93a8 to your computer and use it in GitHub Desktop.
Save fabb/f280521420a421af93a8 to your computer and use it in GitHub Desktop.
//some time, frc section may be need section offset
@objc public protocol MFetchedResultsControllerOffsetSectionDelegate{
func offsetSection() -> Int
}
class MFetchedResultsController: NSFetchedResultsController, NSFetchedResultsControllerDelegate {
weak var viewController: UIViewController? //UITableViewController UICollectionViewController
weak var scrollView: UIScrollView? //TableView CollectionView
weak var offsetSectionDelegate: MFetchedResultsControllerOffsetSectionDelegate?
private var offsetSection: Int!
var deletedSectionIndexes: NSMutableIndexSet!
var insertedSectionIndexes: NSMutableIndexSet!
var deletedRowIndexPaths: [NSIndexPath]!
var insertedRowIndexPaths: [NSIndexPath]!
var updatedRowIndexPaths: [NSIndexPath]!
//if frc is alloc after viewWillAppear, you should set it to false menually
var isViewInvisble: Bool = true
//in viewWillAppear call
func startListening(){
//for first time viewWillAppear, unnecessary performFetch and reloadData
if isViewInvisble {
isViewInvisble = false
return
}
if let _ = viewController {
self.delegate = self
do {
try self.performFetch()
if let t = scrollView as? UITableView {
t.reloadData()
}else if let c = scrollView as? UICollectionView {
c.reloadData()
}
} catch (_){
}
}
}
//in viewWillDisappear call
func endListening(){
self.delegate = nil
}
// MARK: NSFetchedResultsControllerDelegate
func controllerWillChangeContent(controller: NSFetchedResultsController){
assert(viewController != nil, "viewController is nil")
assert(scrollView != nil, "scrollView is nil")
if viewController == nil || scrollView == nil || DataEnvironment.sharedDataEnvironment().notNeedUpdateVC == viewController || !viewController!.isViewLoaded() {
return
}
print("FRC VC:", viewController);
if let d = self.offsetSectionDelegate {
offsetSection = d.offsetSection()
}else{
offsetSection = 0
}
insertedSectionIndexes = NSMutableIndexSet()
deletedSectionIndexes = NSMutableIndexSet()
insertedRowIndexPaths = [NSIndexPath]()
deletedRowIndexPaths = [NSIndexPath]()
updatedRowIndexPaths = [NSIndexPath]()
}
func controller(controller: NSFetchedResultsController, didChangeSection sectionInfo: NSFetchedResultsSectionInfo, atIndex sectionIndex: Int, forChangeType type: NSFetchedResultsChangeType){
if viewController == nil || scrollView == nil || DataEnvironment.sharedDataEnvironment().notNeedUpdateVC == viewController || !viewController!.isViewLoaded() {
return
}
switch type {
case .Insert:
self.insertedSectionIndexes.addIndex(sectionIndex+offsetSection)
case .Delete:
self.deletedSectionIndexes.addIndex(sectionIndex+offsetSection)
default: break
}
}
func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?){
if viewController == nil || scrollView == nil || DataEnvironment.sharedDataEnvironment().notNeedUpdateVC == viewController || !viewController!.isViewLoaded() {
return
}
print(type.rawValue, indexPath, newIndexPath)
if let o = anObject as? NSManagedObject {
print(o.entity.name)
}
switch type {
case .Insert:
if indexPath == nil { //fix iOS8 bug
let section = newIndexPath!.section+offsetSection
//avoid insert row again if section inserted
if !self.insertedSectionIndexes.containsIndex(section) {
self.insertedRowIndexPaths!.append(NSIndexPath(forRow: newIndexPath!.row, inSection: newIndexPath!.section+offsetSection))
}
}
case .Delete:
let section = indexPath!.section+offsetSection
//avoid delete row again if section deleted
if !self.deletedSectionIndexes.containsIndex(section) {
self.deletedRowIndexPaths.append(NSIndexPath(forRow: indexPath!.row, inSection: indexPath!.section+offsetSection))
}
case .Move:
if let i = indexPath, nI = newIndexPath {
if i.row != nI.row || i.section != nI.section { //fix iOS9 bug
if !self.deletedSectionIndexes.containsIndex(i.section) {
self.deletedRowIndexPaths.append(NSIndexPath(forRow: i.row, inSection: i.section+offsetSection))
}
if !self.insertedSectionIndexes.containsIndex(nI.section){
self.insertedRowIndexPaths.append(NSIndexPath(forRow: nI.row, inSection: nI.section+offsetSection))
}else{
assert(false, "there is another bug")
}
}
}
case .Update:
let ip = NSIndexPath(forRow: indexPath!.row, inSection: indexPath!.section+offsetSection)
self.updatedRowIndexPaths.append(ip)
}
}
func controllerDidChangeContent(controller: NSFetchedResultsController){
defer {
//clean
self.deletedSectionIndexes = nil
self.insertedSectionIndexes = nil
self.deletedRowIndexPaths = nil
self.insertedRowIndexPaths = nil
self.updatedRowIndexPaths = nil
}
if viewController == nil || scrollView == nil || DataEnvironment.sharedDataEnvironment().notNeedUpdateVC == viewController || !viewController!.isViewLoaded() {
return
}
print("insert section: ", self.insertedSectionIndexes)
print("delete section: ", self.deletedSectionIndexes)
print("insert indpath: ", self.insertedRowIndexPaths)
print("delete indpath: ", self.deletedRowIndexPaths)
print("update indpath: ", self.updatedRowIndexPaths)
//fix iOS8 bug: update and then move same indexPath
self.updatedRowIndexPaths = self.updatedRowIndexPaths.filter {
return !self.insertedSectionIndexes.contains($0.section) &&
!self.deletedSectionIndexes.contains($0.section) &&
!self.insertedRowIndexPaths.contains($0) &&
!self.deletedRowIndexPaths.contains($0)
}
print("update indpath: ", self.updatedRowIndexPaths)
let c = self.insertedSectionIndexes.count +
self.deletedSectionIndexes.count +
self.insertedRowIndexPaths.count +
self.deletedRowIndexPaths.count +
self.updatedRowIndexPaths.count
if c > 0 { //fix iOS8/9 bug: count maybe 0
if let tableView = scrollView as? UITableView {
tableView.beginUpdates()
//for section
if self.deletedSectionIndexes.count > 0 {
tableView.deleteSections(self.deletedSectionIndexes, withRowAnimation: UITableViewRowAnimation.Automatic)
}
if self.insertedSectionIndexes.count > 0 {
tableView.insertSections(self.insertedSectionIndexes, withRowAnimation: UITableViewRowAnimation.Automatic)
}
//for row
if self.updatedRowIndexPaths.count > 0 { //put update first
tableView.reloadRowsAtIndexPaths(self.updatedRowIndexPaths, withRowAnimation: UITableViewRowAnimation.None)
}
if self.deletedRowIndexPaths.count > 0 {
tableView.deleteRowsAtIndexPaths(self.deletedRowIndexPaths, withRowAnimation: UITableViewRowAnimation.Automatic)
}
if self.insertedRowIndexPaths.count > 0 {
tableView.insertRowsAtIndexPaths(self.insertedRowIndexPaths, withRowAnimation: UITableViewRowAnimation.Automatic)
}
tableView.endUpdates()
}else if let collectionView = scrollView as? UICollectionView {
collectionView.performBatchUpdates({ () -> Void in
if self.deletedSectionIndexes.count > 0 {
collectionView.deleteSections(self.deletedSectionIndexes)
}
if self.insertedSectionIndexes.count > 0 {
collectionView.insertSections(self.insertedSectionIndexes)
}
if self.deletedRowIndexPaths.count > 0 {
collectionView.deleteItemsAtIndexPaths(self.deletedRowIndexPaths)
}
if self.insertedRowIndexPaths.count > 0 {
collectionView.insertItemsAtIndexPaths(self.insertedRowIndexPaths)
}
if self.updatedRowIndexPaths.count > 0 {
collectionView.reloadItemsAtIndexPaths(self.updatedRowIndexPaths)
}
}, completion: { (_) -> Void in
})
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment