如何使用核心数据实现表格部分

Posted

技术标签:

【中文标题】如何使用核心数据实现表格部分【英文标题】:How to implement table sections with core data 【发布时间】:2013-11-27 17:25:32 【问题描述】:

我在我的 ios 应用程序中使用核心数据。我有一个表格视图来显示来自名为人员的实体的数据。实体persons有几个属性,其中一个是“日期”。我想在表格视图中创建 5 个永久部分(0.Today、1.Tomorrow、2.This week、3.This month、4.Upcoming),然后我希望根据每个部分显示数据日期。 我一直在寻找这个问题,但没有发现可以应用于我的应用程序。 恳请您告诉我正确的处理方法。

这是我目前的代码:

#import "PersonsTVC.h"
#import "Person.h"

@implementation PersonsTVC
@synthesize fetchedResultsController = __fetchedResultsController;
@synthesize managedObjectContext = __managedObjectContext;
@synthesize selectedPerson;
@synthesize searchResults,titulosseccion;

- (void)setupFetchedResultsController

    // 1 - Decide what Entity you want
    NSString *entityName = @"Person"; // Put your entity name here
    NSLog(@"Setting up a Fetched Results Controller for the Entity named %@", entityName);

    // 2 - Request that Entity
    NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:entityName];

    // 3 - Filter it if you want
    //request.predicate = [NSPredicate predicateWithFormat:@"Person.name = Blah"];

    // 4 - Sort it if you want
    request.sortDescriptors = [NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:@"firstname"
                                                                                     ascending:YES
                                                                                      selector:@selector(localizedCaseInsensitiveCompare:)]];
    // 5 - Fetch it
    self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:request
                                                                        managedObjectContext:self.managedObjectContext
                                                                          sectionNameKeyPath:nil
                                                                                   cacheName:nil];
    [self performFetch];




- (void) viewDidLoad




    self.searchResults = [NSMutableArray arrayWithCapacity:[[self.fetchedResultsController fetchedObjects] count]];
    [self.tableView reloadData];






-(void) viewDidUnload
    self.searchResults = nil;




- (void)viewWillAppear:(BOOL)animated

    [super viewWillAppear:animated];
    [self setupFetchedResultsController];


- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView 
    return [[self.fetchedResultsController sections] count];





- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath


    // Perform segue to detail when a SEARCH table cell is touched
    if(tableView == self.searchDisplayController.searchResultsTableView)
    
        [self performSegueWithIdentifier:@"Person Detail Segue" sender:tableView];
    





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

    static NSString *CellIdentifier = @"Persons Cell";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) 
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
    

    // Configure the cell...
    // Configure the cell...
    Person  *person = nil;

    if (tableView == self.searchDisplayController.searchResultsTableView)
    
        NSLog(@"Configuring cell to show search results");
        person = [self.searchResults objectAtIndex:indexPath.row];
    
    else
    
        NSLog(@"Configuring cell to show normal data");
        person = [self.fetchedResultsController objectAtIndexPath:indexPath];
    








    NSString *fullname = [NSString stringWithFormat:@"%@ %@", person.firstname, person.surname];
    cell.textLabel.text = person.firstname;
    if ([person.inRole.color isEqual :@"Yellow"])
    
        cell.imageView.image = [UIImage imageNamed:@"Yellow"];
    
    if ([person.inRole.color isEqual :@"Black"])
    
        cell.imageView.image = [UIImage imageNamed:@"Black"];
    
    if ([person.inRole.color isEqual :@"Grey"])
    
        cell.imageView.image = [UIImage imageNamed:@"Grey"];
    
    if ([person.inRole.color isEqual :@"Red"])
    
        cell.imageView.image = [UIImage imageNamed:@"Red"];
    
    if ([person.inRole.color isEqual :@"Blue"])
    
        cell.imageView.image = [UIImage imageNamed:@"Blue"];
    
    if ([person.inRole.color isEqual :@"Dark Green"])
    
        cell.imageView.image = [UIImage imageNamed:@"DarkGreen"];
    
    if ([person.inRole.color isEqual :@"Light Green"])
    
        cell.imageView.image = [UIImage imageNamed:@"LightGreen"];
    
    if ([person.inRole.color isEqual :@"Light Blue"])
    
        cell.imageView.image = [UIImage imageNamed:@"LightBlue"];
    
    if ([person.inRole.color isEqual :@"Brown"])
    
        cell.imageView.image = [UIImage imageNamed:@"Brown"];
    
    if ([person.inRole.color isEqual :@"Dark Orange"])
    
        cell.imageView.image = [UIImage imageNamed:@"DarkOrange"];



    

    NSDate *fechasinformat = person.date;
    NSString *fecha0 = [NSString stringWithFormat:@"%@", fechasinformat];


   cell.detailTextLabel.text = fecha0;



    return cell;



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

    if (tableView == self.searchDisplayController.searchResultsTableView)
    
        return [self.searchResults count];
    
    else
    
        return [[[self.fetchedResultsController sections] objectAtIndex:section] numberOfObjects];
    



- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath 

    if (editingStyle == UITableViewCellEditingStyleDelete) 

        [self.tableView beginUpdates]; // Avoid  NSInternalInconsistencyException

        // Delete the person object that was swiped
        Person *personToDelete = [self.fetchedResultsController objectAtIndexPath:indexPath];
        NSLog(@"Deleting (%@)", personToDelete.firstname);
        [self.managedObjectContext deleteObject:personToDelete];
        [self.managedObjectContext save:nil];

        // Delete the (now empty) row on the table
        [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
        [self performFetch];

        [self.tableView endUpdates];
    


- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender

    if ([segue.identifier isEqualToString:@"Add Person Segue"])
    
        NSLog(@"Setting PersonsTVC as a delegate of PersonDetailTVC");
        PersonDetailTVC *personDetailTVC = segue.destinationViewController;
        personDetailTVC.delegate = self;

        NSLog(@"Creating a new person and passing it to PersonDetailTVC");
        Person *newPerson = [NSEntityDescription insertNewObjectForEntityForName:@"Person"
                                                          inManagedObjectContext:self.managedObjectContext];

        personDetailTVC.person = newPerson;
    
    else if ([segue.identifier isEqualToString:@"Person Detail Segue"])
    
        NSLog(@"Setting PersonsTVC as a delegate of PersonDetailTVC");
        PersonDetailTVC *personDetailTVC = segue.destinationViewController;
        personDetailTVC.delegate = self;

        // Store selected Person in selectedPerson property
        if(sender == self.searchDisplayController.searchResultsTableView)
        
            NSIndexPath *indexPath = [self.searchDisplayController.searchResultsTableView indexPathForSelectedRow];
            self.selectedPerson = [self.searchResults objectAtIndex:[indexPath row]];
        
        else
        
            NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
            self.selectedPerson = [self.fetchedResultsController objectAtIndexPath:indexPath];
        

        NSLog(@"Passing selected person (%@) to PersonDetailTVC",    self.selectedPerson.firstname);
        personDetailTVC.person = self.selectedPerson;
    
    else
    
        NSLog(@"Unidentified Segue Attempted!");
    


- (void)theSaveButtonOnThePersonDetailTVCWasTapped:(PersonDetailTVC *)controller

    // do something here like refreshing the table or whatever

    // close the delegated view
    [controller.navigationController popViewControllerAnimated:YES];    


#pragma mark -
#pragma mark Content Filtering

-(void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope 
    self.searchResults = [[self.fetchedResultsController fetchedObjects] filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) 
        Person* person = evaluatedObject;
        NSString* firstName = person.firstname;

        //searchText having length < 3 should not be considered
        if (!!searchText && [searchText length] < 3) 
            return YES;
        

        if ([scope isEqualToString:@"All"] || [firstName isEqualToString:scope])  
            return ([firstName rangeOfString:searchText].location != NSNotFound);
        
        return NO; //if nothing matches
    ]];


#pragma mark -
#pragma mark UISearchDisplayController Delegate Methods

- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString

    [self filterContentForSearchText:searchString scope:@"All"];
    return YES;


- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchScope:(NSInteger)searchOption

    [self filterContentForSearchText:[self.searchDisplayController.searchBar text] scope:@"All"];
    return YES;




@end

【问题讨论】:

【参考方案1】:

您将不得不大致执行以下操作:

date 属性上的第一个排序描述符添加到获取请求。

向 Person 实体添加 transient 属性“sectionIdentifier”, 并实现自定义 getter - (NSString *)sectionIdentifierPerson 托管对象子类,它返回 “0”、“1”、“2”、“3”或“4”,具体取决于对象的date 属性。

在获取结果控制器的创建中设置sectionNameKeyPath:@"sectionIdentifier"

向表视图控制器添加一个titleForHeaderInSection 方法,即 根据部分返回“今天”、“明天”、...。

Apple 开发者库中的 DateSectionTitles 示例项目也演示了它是如何工作的。

排序描述符将如下所示:

// First sort descriptor (required for grouping into sections):
NSSortDescriptor *sortByDate = [[NSSortDescriptor alloc] initWithKey:@"date" ascending:YES];
// Second sort descriptor (for the items within each section):
NSSortDescriptor *sortByName = [[NSSortDescriptor alloc] initWithKey:@"firstname" ascending:YES];
[request setSortDescriptors:@[sortByDate, sortByName]];

瞬态“sectionIdentifier”属性的 getter 方法如下所示 (改编自“DateSectionTitles”示例代码):

- (NSString *)sectionIdentifier

    [self willAccessValueForKey:@"sectionIdentifier"];
    NSString *tmp = [self primitiveValueForKey:@"sectionIdentifier"];
    [self didAccessValueForKey:@"sectionIdentifier"];

    if (!tmp)
    
        NSDate *date = self.date;
        // Using pseudo-code here:
        if ("date is from today") 
            tmp = @"0";
         else if ("date is from tomorrow") 
            tmp = @"1";
         else ... // and so on ...

        [self setPrimitiveValue:tmp forKey:@"sectionIdentifier"];
    
    return tmp;

要确定日期是否在今天、明天等,您必须使用NSCalendar 方法。

titleForHeaderInSection 方法与此类似(未经测试,如 此答案中的所有其他内容):

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

    id <NSFetchedResultsSectionInfo> theSection = [[self.fetchedResultsController sections] objectAtIndex:section];

    NSString *sectionName = [theSection name];
    if ([sectionName isEqualToString:@"0"]) 
        return @"Today";
     else if ([sectionName isEqualToString:@"1"]) 
        return @"Tomorrow";
     ... // and so on ...
     else 
        return @"Other";
    

【讨论】:

感谢 Martin R,我已经下载并测试了您告诉我的示例项目,但我看不到如何使用它。非常抱歉,但我不是专业的开发人员,我正在为自己做应用程序,学习如何做事。你建议我在获取请求的日期属性上添加一个第一排序描述符......你能帮我一步一步地完成这个,这不仅对我有帮助,而且对其他试图学习的人都有帮助。 我可以理解您列出的所有步骤,但我不知道实施它们的确切热度。我不想让你为我编写代码,但是你可以举一个例子来做这件事。再次感谢您。 @mvasco:我添加了更多信息,希望对您有所帮助。 再次感谢马丁,我现在将尝试实施您的建议。你帮了我很多。完成后我会告诉你。 Martin,我在以下代码中收到“No visible @interface”错误:[self willAccessValueForKey:@"sectionIdentifier"]; NSString *tmp = [自原始SectionIdentifier]; [self didAccessValueForKey:@"sectionIdentifier"];

以上是关于如何使用核心数据实现表格部分的主要内容,如果未能解决你的问题,请参考以下文章

重新排列表格视图单元格后如何更新核心数据记录

在 blazor 应用程序中使用 mudblazor 实现通用表格组件。 .net 核心

如何使用核心数据离线显示 Json 数据

在表格视图部分中对核心数据对象进行排序

更新表格视图单元格中的核心数据值后如何刷新

使用 MKMapView、核心位置和核心数据