简体   繁体   中英

CoreData TableView inside UIViewController

I want to re-write a view I made in my app to improve the layout. Originally I implemented a TableViewController that uses CoreData as its source. This view worked well for me until I needed to add more views in the TableViewController that would act indipendent of the table and don't scroll out of the screen as more rows are added. The initial implemetnation is as follows based on the apple guide to 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

In my TableViewController, called CDTVC.h, I created a subclass that inherits from CoreDataTableViewController:

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

In my 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
}

Per guidelines, I need to implement this myself as it is not handled by the parent:

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

So now my view controller is no longer the TableViewController, but a ViewController that contains a TableView as such:

在此处输入图片说明

So I introduced a new View Controller, and this is where I feel what I did is a hack and not quite as elegant as the solution could be:

In CDTVC.h, I added the following so I can access data in the TallyViewController:

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

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

In CDTVC.m, I added the following:

@synthesize dataSource = _dataSource;

I moved loadTally implementation to TallyViewController.

TallyViewController.h now has to inherit from UIViewController, which means I cannot inherit from CoreDataViewController anymore:

@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;

So to work around this multiple inheritance issue, I decided to allocate my own instance of CDTVCTable in my TallyViewController.m:

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

I also had to implement all of the table view delegates but redirect them to my CDTVCTable, where as before I only had to implement 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];
}

So the question is, is there a more elegant way to implement this with delegates and data sources. I have to admit my grasp on delegates and datasources is weak so I am not sure to outline this in a better way.

If you need / want to mix static views elements with a UITableview, then I find it's usually easiest to add a container view to your ViewController and connect an external UITableview to it.

Send the forward data from your static ViewController via the Segue to the UITableView and in return use a delegate to let the static ViewController know what happened in the UITableview.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM