如何在多个 ViewController 中使用 NSFetchedResultsControllerDelegate 而不重复控制器代码?
Posted
技术标签:
【中文标题】如何在多个 ViewController 中使用 NSFetchedResultsControllerDelegate 而不重复控制器代码?【英文标题】:How do I use NSFetchedResultsControllerDelegate in multiple ViewController without repeating the controller code? 【发布时间】: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()
提前致谢:)
【问题讨论】:
【参考方案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)和获取控制器委托。值得庆幸的是,有一个简单的解决方案是使用一种称为组合的技术。将复制的表放入实现NSFetchedResultsControllerDelegate
的UITableViewController
子类中并获取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;
【讨论】:
以上是关于如何在多个 ViewController 中使用 NSFetchedResultsControllerDelegate 而不重复控制器代码?的主要内容,如果未能解决你的问题,请参考以下文章
如何在多个 ViewController 中使用 NSFetchedResultsControllerDelegate 而不重复控制器代码?
如何在一个 viewController 中管理多个计时器?
在多个 viewController 中显示的通用或单个 uipickerview