Просмотр таблиц CoreData внутри UIViewController

#ios #objective-c #uitableview #core-data #uiviewcontroller

#iOS #objective-c #uitableview #core-данные #uiviewcontroller

Вопрос:

Я хочу переписать представление, которое я создал в своем приложении, чтобы улучшить макет. Первоначально я реализовал TableViewController, который использует CoreData в качестве своего источника. Это представление хорошо работало для меня, пока мне не понадобилось добавить больше представлений в TableViewController, которые действовали бы независимо от таблицы и не прокручивались за пределы экрана по мере добавления новых строк. Начальная реализация выглядит следующим образом на основе руководства Apple по 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
 

В моем TableViewController, который называется CDTVC.h, я создал подкласс, который наследуется
от 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, а ViewController, который содержит TableView как таковой:

введите описание изображения здесь

Итак, я представил новый контроллер представления, и именно здесь я чувствую, что то, что я сделал, является взломом и не таким элегантным, каким могло бы быть решение:

В CDTVC.h я добавил следующее, чтобы я мог получить доступ к данным в TallyViewController:

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

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

В CDTVC.m я добавил следующее:

 @synthesize dataSource = _dataSource;
 

Я переместил загрузочную реализацию в 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;
 

Поэтому, чтобы обойти эту проблему множественного наследования, я решил выделить свой
собственный экземпляр CDTVCTable в моем 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.
}
 

Мне также пришлось реализовать все делегаты табличного представления, но перенаправить их на
мою 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. Это слишком много, чтобы переварить. У вас будет больше шансов получить достойные ответы, если вы сможете сформулировать свой вопрос более кратко.

2. Я хотел дать полную картину, чтобы избежать однострочного ответа: используйте делегат, где я не совсем понимаю, как, если это подход.

Ответ №1:

Если вам нужно / хотите смешивать элементы статических представлений с UITableView, то я считаю, что обычно проще всего добавить представление контейнера в ваш ViewController и подключить к нему внешний UITableView.

Отправьте прямые данные из вашего статического ViewController через Segue в UITableView и, в свою очередь, используйте делегат, чтобы сообщить статическому ViewController, что произошло в UITableView.

Комментарии:

1. Спасибо, но это не сильно отличается от того, что я делаю. Моя проблема в том, что когда я использую UITableView вместо UITableViewController, поведение основных данных отличается, и это то, что я изо всех сил пытаюсь легко интегрировать.

2. Спасибо за идею, хотя я думаю, что концепция контейнеров может быть очень полезной в будущем!

3. К моему удовольствию, я нахожу это очень удобным, особенно при представлении модальных представлений из указанного UITableViewController.