简体   繁体   English

如何在多个ViewController中使用NSFetchedResultsControllerDelegate而不重复控制器代码?

[英]How do I use NSFetchedResultsControllerDelegate in multiple ViewController without repeating the controller code?

I am making an app that uses Core Data, it happens that in this app I have three tableViews populated with different data and all of them are in different controllers, to avoid repeating the code (since it is exactly the same) I would like to have a solution for this. 我正在制作一个使用Core Data的应用程序,碰巧在这个应用程序中,我有三个用不同数据填充的tableView,并且它们都在不同的控制器中,以避免重复代码(因为它完全相同),我想有一个解决方案。

I have tried through protocols and with a MainController that inherits from UIViewController, which in turn had the extension of NSFetchedResultsControllerDelegate. 我已经尝试过协议,并且使用了从UIViewController继承的MainController,后者又扩展了NSFetchedResultsControllerDelegate。 Im almost positive that doing a super class where I can put this code is the solution, but since I'm new to swift and to programming and I'm also positive that there is something that im not doing as it was supposed. 我几乎肯定地说,可以在其中放置此代码的超类是解决方案,但是由于我是快速入门和编程的新手,所以我也很肯定有些即时消息没有按照预期执行。

In the parent class I've made something like this: 在父类中,我做了这样的事情:

class MainController: UIViewController, NSFetchedResultsController {

     var activeTableView: UITableView?
}

And in the child: 而在孩子中:

class SecdController: MainController {

   @IBOutlet weak var tableView: UITableView!

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

Below is the code I want to be called three controllers. 下面是我要称为三个控制器的代码。

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

Thanks in advance :) 提前致谢 :)

Your MainController should handle NSFetchedResultsControllerDelegate instead of inherit NSFetchedResultsController 您的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()
    }
}

And then in the other view controller just override setupFetchedResultsController() and setup the fetch results controller: 然后在另一个视图控制器中,只需重写setupFetchedResultsController()并设置获取结果控制器:

class SecondController: MainController {

   override func createFetchResultController() {

        let fetchRequest: NSFetchRequest<...> = ...
        let sortDescriptor = NSSortDescriptor(key: "name", ascending: true)
        fetchRequest.sortDescriptors = [sortDescriptor]

        fetchedResultsController = ...
    }
}

This is a tricky one because the normal solutions of refactoring the delegates to its own class don't work because the class needs to handle both the table delegate (ie be a UITableViewController) and also be a fetch controller delegate. 这是一个棘手的问题,因为将委托重构为其自己的类的常规解决方案不起作用,因为该类既需要处理表委托(即,是UITableViewController),又需要处理获取控制器委托。 Thankfully there is a simple solution which is to use a technique called composition. 值得庆幸的是,有一个简单的解决方案是使用一种称为合成的技术。 Put the duplicated table and fetch in a UITableViewController subclass that implements NSFetchedResultsControllerDelegate and make a fetchedResultsController property (pictured on right in image below). 把复制表,并在获取UITableViewController实现子类NSFetchedResultsControllerDelegate并作出fetchedResultsController属性(在下面的图像描绘上右图)。

For the code that is different put that in several UIViewController subclasses (pictured on left in image below) for each specific case. 对于不同的代码,针对每种特定情况,将其放在几个UIViewController子类中(如左图所示)。

在此处输入图片说明

Use an embed segue to make this view controller contain the table controller. 使用embed segue使该视图控制器包含表控制器。 In the viewDidLoad create the fetch controller and set it to the table controller. viewDidLoad创建访viewDidLoad控制器并将其设置为表控制器。 eg 例如

- (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; 
}

If you want to handle some specific table or fetch delegate methods then a nifty way is to set the view controller as the delegates, and then forward any of the method calls on to the embedded table controller. 如果要处理某些特定的表或获取委托方法,则一种不错的方法是将视图控制器设置为委托,然后将任何方法调用转发到嵌入式表控制器。 eg 例如

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

In that case you should also implement the forwardingTargetForSelector pattern to make sure everything else is forwarded on too. 在这种情况下,您还应该实现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;
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何在多个ViewController中使用自定义工具栏,而无需重复代码和复制元素(Swift)? - how to use customized toolbar in multiple viewcontroller without repeating the code and copying the elements (swift)? 在制作没有ViewController的应用程序时如何使用RootViewController? - How do I use a RootViewController when making an app without a ViewController? 我如何使用多个文件在viewController中访问它们? - How do i use multiple files to access them in viewController? 如何在没有 NSFetchedResultsControllerDelegate 的情况下获取结果? - How to fetch results without NSFetchedResultsControllerDelegate? 如何使用Swift单元测试NSFetchedResultsControllerDelegate(controllerWillChangeContent和controllerDidChangeContent) - How do I unit test NSFetchedResultsControllerDelegate (controllerWillChangeContent and controllerDidChangeContent) using Swift 如何处理自定义结果视图的NSFetchedResultsControllerDelegate更新 - How do I Handle NSFetchedResultsControllerDelegate updates for custom results view 如何为通过导航控制器连接的视图添加代码到viewcontroller.swift? - How do I add code to my viewcontroller.swift for views connected via a navigation controller? Swift 3-在每个ViewController上将BarButtonItem添加到NavigationBar而不重复代码 - Swift 3 - Add BarButtonItem To NavigationBar On Each ViewController Without Repeating Code 如何找到属于当前viewController的导航控制器? - How do I find out the navigation controller that is part of the current viewController? 如何在不与View Controller结合的情况下在iOS上使用GoogleSignIn? - How do I use GoogleSignIn on iOS without coupling with the View Controller?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM