【问题标题】:How do I use NSFetchedResultsControllerDelegate in multiple ViewController without repeating the controller code?如何在多个 ViewController 中使用 NSFetchedResultsControllerDelegate 而不重复控制器代码?
【发布时间】:2019-09-16 07:20:51
【问题描述】:

我正在制作一个使用 Core Data 的应用程序,碰巧在这个应用程序中我有三个 tableViews 填充了不同的数据,并且它们都在不同的控制器中,以避免重复代码(因为它完全相同)我想要解决这个问题。

我尝试过通过协议和继承自 UIViewController 的 MainController,后者又具有 NSFetchedResultsControllerDelegate 的扩展。 我几乎肯定的是,我可以在其中放置此代码的超级类是解决方案,但由于我是 swift 和编程的新手,而且我也很肯定有些事情我没有按照预期的那样做。

在父类中我做了这样的事情:

class MainController: UIViewController, NSFetchedResultsController {

     var activeTableView: UITableView?
}

在孩子身上:

class SecdController: MainController {

   @IBOutlet weak var tableView: UITableView!

   override var activeTableView: UITableView? {
     get {return tableView}
     set {}
  }
}

下面是我要称为三个控制器的代码。

extension SomeController: NSFetchedResultsControllerDelegate {

    func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {

        switch type {

        case .insert:
            tableView.insertRows(at: [newIndexPath!], with: .top)
            break
        case .delete:
            tableView.deleteRows(at: [indexPath!], with: .fade)
            break
        case .update:
            tableView.reloadRows(at: [indexPath!], with: .top)
        case .move:
            tableView.reloadRows(at: [indexPath!], with: .top)
        default:
            fatalError("feature not yet implemented")
        }
    }

    func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange sectionInfo: NSFetchedResultsSectionInfo, atSectionIndex sectionIndex: Int, for type: NSFetchedResultsChangeType) {

        let indexSet = IndexSet(integer: sectionIndex)

        switch type {

        case .insert:
            tableView.insertSections(indexSet, with: .top)
        case .delete:
            tableView.deleteSections(indexSet, with: .top)
        case .update, .move:
            fatalError("Invalid change.")
        default:
            fatalError("feature not yet implemented")
        }
    }

    func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {

        tableView.beginUpdates()
    }
    func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {

        tableView.endUpdates()
    }
}

提前致谢:)

【问题讨论】:

    标签: ios swift inheritance core-data nsfetchedresultscontroller


    【解决方案1】:

    您的MainController 应该处理NSFetchedResultsControllerDelegate 而不是继承NSFetchedResultsController

    
    class MainController: UIViewController {
    
        @IBOutlet weak var tableView: UITableView!
        var fetchedResultsController: NSFetchedResultsController<NSManagedObject>?
    
    override func viewDidLoad() {
            super.viewDidLoad()
    
            setupFetchedResultsController()
    
           fetchedResultsController?.delegate = self
           // perform fetch
        }
    
    func setupFetchedResultsController() {
            preconditionFailure("The method must be overridden and initialize the fetch result controller.")
        }
    }
    
    extension MainController: NSFetchedResultsControllerDelegate {
    
        func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
    
    // Do not force unwrap
            switch type {
    
            case .insert:
                tableView.insertRows(at: [newIndexPath!], with: .top)
                break
            case .delete:
                tableView.deleteRows(at: [indexPath!], with: .fade)
                break
            case .update:
                tableView.reloadRows(at: [indexPath!], with: .top)
            case .move:
                tableView.reloadRows(at: [indexPath!], with: .top)
            default:
                fatalError("feature not yet implemented")
            }
        }
    
        func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange sectionInfo: NSFetchedResultsSectionInfo, atSectionIndex sectionIndex: Int, for type: NSFetchedResultsChangeType) {
    
            let indexSet = IndexSet(integer: sectionIndex)
    
            switch type {
    
            case .insert:
                tableView.insertSections(indexSet, with: .top)
            case .delete:
                tableView.deleteSections(indexSet, with: .top)
            case .update, .move:
                fatalError("Invalid change.")
            default:
                fatalError("feature not yet implemented")
            }
        }
    
        func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
    
            tableView.beginUpdates()
        }
        func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
    
            tableView.endUpdates()
        }
    }
    
    

    然后在另一个视图控制器中覆盖setupFetchedResultsController() 并设置获取结果控制器:

    class SecondController: MainController {
    
       override func createFetchResultController() {
    
            let fetchRequest: NSFetchRequest<...> = ...
            let sortDescriptor = NSSortDescriptor(key: "name", ascending: true)
            fetchRequest.sortDescriptors = [sortDescriptor]
    
            fetchedResultsController = ...
        }
    }
    

    【讨论】:

    • 先生,您救了我 :) 但是我必须为每个实体使用 fetchedResultsController,泛型类型需要更多时间,我没有的宝贵时间。编译器迫使我放弃大量的东西:(以及错误和错误
    【解决方案2】:

    这是一个棘手的问题,因为将委托重构为自己的类的常规解决方案不起作用,因为该类需要处理表委托(即作为 UITableViewController)和获取控制器委托。值得庆幸的是,有一个简单的解决方案是使用一种称为组合的技术。将复制的表放入实现NSFetchedResultsControllerDelegateUITableViewController 子类中并获取fetchedResultsController 属性(如下图右侧所示)。

    对于不同的代码,请针对每个特定情况将其放入多个UIViewController 子类(如下图左侧)。

    使用嵌入转场使该视图控制器包含表控制器。在viewDidLoad 中创建获取控制器并将其设置为表控制器。例如

    - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
        if([segue.identifier isEqualToString:@"embed"]){
            self.fetchedTableViewController = segue.destinationViewController;
        }
    }
    
    - (void)viewDidLoad {
        [super viewDidLoad];
    
        self.fetchedTableViewController.fetchedResultsController = self.fetchedResultsController;
    
        // optional for more customization
        self.fetchedTableViewController.tableView.delegate = self;
        self.fetchedTableViewController.tableView.dataSource = self; 
    }
    

    如果您想处理某些特定的表或获取委托方法,那么一个不错的方法是将视图控制器设置为委托,然后将任何方法调用转发到嵌入式表控制器。例如

    - (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
        [self.fetchedTableViewController controllerDidChangeContent:controller];
    

    在这种情况下,您还应该实现forwardingTargetForSelector 模式以确保其他所有内容也被转发。

    - (id)forwardingTargetForSelector:(SEL)aSelector{
        if(MHFProtocolHasInstanceMethod(@protocol(UITableViewDelegate), aSelector)){
            if([self.fetchedTableViewController respondsToSelector:aSelector]){
                return self.fetchedTableViewController;
            }
        }
        else if(MHFProtocolHasInstanceMethod(@protocol(UITableViewDataSource), aSelector)){
            if([self.fetchedTableViewController respondsToSelector:aSelector]){
                return self.fetchedTableViewController;
            }
        }
        else if(MHFProtocolHasInstanceMethod(@protocol(NSFetchedResultsControllerDelegate), aSelector)){
            if([self.fetchedTableViewController respondsToSelector:aSelector]){
                return self.fetchedTableViewController;
            }
        }
        else if(MHFProtocolHasInstanceMethod(@protocol(UIDataSourceModelAssociation), aSelector)){
            if([self.fetchedTableViewController respondsToSelector:aSelector]){
                return self.fetchedTableViewController;
            }
        }
        return [super forwardingTargetForSelector:aSelector];
    }
    
    - (BOOL)respondsToSelector:(SEL)aSelector{
        if([super respondsToSelector:aSelector]){
            return YES;
        }
        else if(MHFProtocolHasInstanceMethod(@protocol(UITableViewDelegate), aSelector)){
            if([self.fetchedTableViewController respondsToSelector:aSelector]){
                return YES;
            }
        }
        else if(MHFProtocolHasInstanceMethod(@protocol(UITableViewDataSource), aSelector)){
            if([self.fetchedTableViewController respondsToSelector:aSelector]){
                return YES;
            }
        }
        else if(MHFProtocolHasInstanceMethod(@protocol(NSFetchedResultsControllerDelegate), aSelector)){
            if([self.fetchedTableViewController respondsToSelector:aSelector]){
                return YES;
            }
        }
        else if(MHFProtocolHasInstanceMethod(@protocol(UIDataSourceModelAssociation), aSelector)){
            if([self.fetchedTableViewController respondsToSelector:aSelector]){
                return YES;
            }
        }
        return NO;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-08-03
      • 2017-08-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多