UIViewController里面的CoreData TableView

Posted

技术标签:

【中文标题】UIViewController里面的CoreData TableView【英文标题】:CoreData TableView inside UIViewController 【发布时间】:2014-06-24 04:44:34 【问题描述】:

我想重写我在应用中创建的视图以改进布局。最初我实现了一个使用 CoreData 作为其源的 TableViewController。这个视图对我来说效果很好,直到我需要在 TableViewController 中添加更多视图,这些视图将独立于表格并且在添加更多行时不会滚动到屏幕外。根据苹果对 NSFetchedResultsController 的指南,初步实现如下:

CoreDataTableViewController.h:

@interface CoreDataTableViewController : UITableViewController <NSFetchedResultsControllerDelegate>
    @property (strong, nonatomic) NSFetchedResultsController *fetchedResultsController;
    - (void)performFetch;
    @property (nonatomic) BOOL suspendAutomaticTrackingOfChangesInManagedObjectContext;
    @property BOOL debug;
@end    

CoreDataTableViewController.m:

@interface CoreDataTableViewController()
    @property (nonatomic) BOOL beganUpdates;
@end

@implementation CoreDataTableViewController

#pragma mark - Properties

@synthesize fetchedResultsController = _fetchedResultsController;
@synthesize suspendAutomaticTrackingOfChangesInManagedObjectContext = _suspendAutomaticTrackingOfChangesInManagedObjectContext;
@synthesize debug = _debug;
@synthesize beganUpdates = _beganUpdates;

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation

    return YES;


#pragma mark - Fetching

- (void)performFetch

<snipped>


- (void)setFetchedResultsController:(NSFetchedResultsController *)newfrc

<snipped>


#pragma mark - UITableViewDataSource

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView

   return [[self.fetchedResultsController sections] count];


- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section


    return [[[self.fetchedResultsController sections] objectAtIndex:section] numberOfObjects];


- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section


return [[[self.fetchedResultsController sections] objectAtIndex:section] name];


- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index

return [self.fetchedResultsController sectionForSectionIndexTitle:title atIndex:index];


- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView


    return [self.fetchedResultsController sectionIndexTitles];


#pragma mark - NSFetchedResultsControllerDelegate
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller

<..snipped..>


- (void)controller:(NSFetchedResultsController *)controller
  didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo
           atIndex:(NSUInteger)sectionIndex
 forChangeType:(NSFetchedResultsChangeType)type

<...snipped...>



- (void)controller:(NSFetchedResultsController *)controller
   didChangeObject:(id)anObject
   atIndexPath:(NSIndexPath *)indexPath
 forChangeType:(NSFetchedResultsChangeType)type
  newIndexPath:(NSIndexPath *)newIndexPath

    if (!self.suspendAutomaticTrackingOfChangesInManagedObjectContext)
    
        switch(type)
        
            case NSFetchedResultsChangeInsert:
                [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
                break;

            case NSFetchedResultsChangeDelete:
                [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
                break;

            case NSFetchedResultsChangeUpdate:
                [self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
                break;

            case NSFetchedResultsChangeMove:
                [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
                [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
                break;
        
    


- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller



- (void)endSuspensionOfUpdatesDueToContextChanges



- (void)setSuspendAutomaticTrackingOfChangesInManagedObjectContext:(BOOL)suspend



@end

在名为 CDTVC.h 的 TableViewController 中,我创建了一个继承 来自 CoreDataTableViewController:

@interface CDTVC : CoreDataTableViewController
    @property (nonatomic, copy) NSString *status;
    @property (nonatomic, copy) NSString *totalTally;
    @property (nonatomic, retain) IBOutlet UILabel *tallyDisplayLabel;
@end

在我的 CDTVC.m 中:

// Gets called when the managedObjectContext is ready
-(void) loadTally

    if (managedObjectContext) 
        NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Entry"];        
        request.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"dateCreated" ascending:NO]];
        request.predicate = nil;            

        self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:request managedObjectContext:managedObjectContext sectionNameKeyPath:nil cacheName:nil];
     else 
        self.fetchedResultsController = nil;
    

    [self performFetch];  // <<----This refreshes the tableview controller

根据指南,我需要自己实施,因为它不是由 家长:

#pragma mark - CoreDataViewController
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"entryCRVPCell"];

    if (cell == nil) 
        UITableViewCellStyle cellStyle = UITableViewCellStyleSubtitle;
        cell = [[UITableViewCell alloc] initWithStyle:cellStyle reuseIdentifier:@"entryCRVPCell"];
    

    TallyEntry *tallyEntry = [self.fetchedResultsController objectAtIndexPath:indexPath];

    if (tallyEntry) 
        NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
        [dateFormatter setDateFormat:@"MMM dd, yyyy"];

        NSString *stringDate = [dateFormatter stringFromDate:tallyEntry.dateCreated];
        cell.textLabel.numberOfLines = 0;
        cell.textLabel.text = [NSString stringWithFormat:@"%@ - %@\n%@", tallyEntry.category, tallyEntry.name, stringDate];
    

    return cell;

所以现在我的视图控制器不再是 TableViewController,而是一个包含 TableView 的 ViewController:

所以我引入了一个新的视图控制器,这就是我觉得我所做的是 一个 hack,并不像解决方案那样优雅:

在 CDTVC.h 中,我添加了以下内容,以便可以访问 TallyViewController:

@protocol TallyViewDataSource
    -(void)setupTallyLabel:(float) tally;
    -(NSString *) getTallyMode;
@end

@property (nonatomic, retain) IBOutlet id <TallyViewDataSource> dataSource;

在 CDTVC.m 中,我添加了以下内容:

@synthesize dataSource = _dataSource;

我将 loadTally 实现移至 TallyViewController。

TallyViewController.h 现在必须从 UIViewController 继承,这意味着我 不能再从 CoreDataViewController 继承:

@interface TallyViewController : UIViewController <UITableViewDataSource, UITableViewDelegate,
TallyViewDataSource> 
     IBOutlet UITableView* myTableView;


@property (nonatomic, retain) IBOutlet UILabel *tallyDisplayLabel;
@property (nonatomic, retain) IBOutlet UITableView *myTableView;
@property (nonatomic, retain) CDTVC* CDTVCTable;

所以为了解决这个多重继承问题,我决定分配我的 在我的 TallyViewController.m 中拥有自己的 CDTVCTable 实例:

- (void)viewDidLoad

    [super viewDidLoad];
    // Do any additional setup after loading the view.
    _CDTVCTable = [[CDTVC alloc] init];
    _CDTVCTable.dataSource = self;


-(void) loadTally

    if (managedObjectContext) 
        NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Entry"];        
        request.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"dateCreated" ascending:NO]];
        request.predicate = nil;            

        _CDTVCTable.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:request managedObjectContext:managedObjectContext sectionNameKeyPath:nil cacheName:nil];
     else 
        _CDTVCTable.fetchedResultsController = nil;
    

    [_CDTVCTable performFetch]; 
    [self.myTableView reload];  //<----Had to add this to refresh the local table.

我还必须实现所有的表视图委托,但将它们重定向到 我的 CDTVCTable,和以前一样,我只需要实现 tableView: cellForRowAtIndexPath:

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView

    return [_CDTVCTable numberOfSectionsInTableView:tableView];


- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 
    return [_CDTVCTable tableView:tableView numberOfRowsInSection:section];


- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

    return [_CDTVCTable tableView:tableView cellForRowAtIndexPath:indexPath];


- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath 
    return [_CDTVCTable tableView:tableView commitEditingStyle:editingStyle forRowAtIndexPath:indexPath];

所以问题是,有没有更优雅的方法来实现它 代表和数据源。我不得不承认我对代表和 数据源很弱,所以我不确定以更好的方式概述这一点。

【问题讨论】:

要消化的东西太多了。如果您能更简洁地陈述您的问题,您将更有可能获得体面的答案。 我想给出一个完整的图片以避免单线答案:使用代表,我不清楚如果这是采取的方法。 【参考方案1】:

如果您需要/想要将静态视图元素与 UITableview 混合,那么我发现将容器视图添加到 ViewController 并将外部 UITableview 连接到它通常是最简单的。

通过 Segue 将来自静态 ViewController 的转发数据发送到 UITableView,然后使用委托让静态 ViewController 知道 UITableview 中发生了什么。

【讨论】:

谢谢,但这与我所做的并没有太大的不同。我的问题是,当我使用 UITableView 而不是 UITableViewController 时,核心数据行为是不同的,这就是我努力无缝集成的原因。 谢谢你的想法,我认为容器的概念在未来可能会非常有用! 我很高兴,我发现它非常方便,尤其是在从上述 UITableViewController 呈现模态视图时。

以上是关于UIViewController里面的CoreData TableView的主要内容,如果未能解决你的问题,请参考以下文章

UIViewController里面的CoreData TableView

TableViewController Vs UIViewController 和里面的 tableView

UIViewControllers 中的 UIViewController

CoreData + UIViewController + UITableView

在 UIViewController 上画一条线

从其子 UIViewControllers 访问父 UIViewController