【问题标题】:Adding extra sections to a NSFetchedResultsController向 NSFetchedResultsController 添加额外的部分
【发布时间】:2010-11-01 16:14:13
【问题描述】:

我正在为我的公司编写一个小型 iPhone 应用程序,该应用程序一次显示每个员工一周的预订情况。我正在使用核心数据来获取给定一周的“预订”列表,并希望将它们显示在 UITableView 中,该 UITableView 分解为一周中的每一天。

问题在于我需要为一周中的每一天显示 7 个部分(在部分/日期没有预订的地方显示“无预订”单元格)。

我有一个应用程序的屏幕截图here(抱歉,我是 StackOverlow 的新手,所以还不能发布图片)

目前,我通过使用“fetchResults”方法来实现这一点,该方法获取预订并将它们组织到一系列可能的日期中:

- (void)refetchResults {    

// Drop bookings Array, replacing with new empty one
// 7 slots for 7 days each holding mutable array to recieve bookings where appropraite
self.bookings = [NSArray arrayWithObjects:[NSMutableArray array],
                  [NSMutableArray array], [NSMutableArray array],
                  [NSMutableArray array], [NSMutableArray array],
                  [NSMutableArray array], [NSMutableArray array], nil];

// Create the fetch request for the entity.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Booking" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];

// Limit to this weeks data
[fetchRequest setPredicate:
 [NSPredicate predicateWithFormat:@"(date >= %@) && (date <= %@) && (resource == %@)",
  firstDate,lastDate,resourceId]];

// Edit the sort key as appropriate.
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"date" ascending:YES];
NSSortDescriptor *sortDescriptor2 = [[NSSortDescriptor alloc] initWithKey:@"recId" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, sortDescriptor2, nil];
[fetchRequest setSortDescriptors:sortDescriptors];

// Fetch records in to array
NSError *error;
NSArray *results = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
if (results == nil) {
    NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
    abort();
}

[fetchRequest release];
[sortDescriptor release];
[sortDescriptor2 release];
[sortDescriptors release];

// Walk through records and place in bookings Array as required
for (Booking *item in results) {
    // Decide on array index by difference in firstDate and booking date
    int idx = (int)[[item date] timeIntervalSinceDate:firstDate]/86400;
    // Add the item to the approp MutArray
    [(NSMutableArray *)[bookings objectAtIndex:idx] addObject:item];
}

// Reload table
[tableView reloadData];

}

我的问题是:有什么方法可以使用 NSFetchedResultsController 达到相同的结果?不知何故,我需要让 NSFetchedResultsController 有 7 个部分,一个用于一周中的每一天,其中一些可能没有预订。

非常感谢任何帮助:)

【问题讨论】:

    标签: iphone objective-c core-data nsfetchedresultscontroller


    【解决方案1】:

    因此,由于外面的天气不太好,我尝试回答自己的问题并实施我在给 westsider 的回复中描述的“解决方法”。

    这个想法是保存一个“映射”数组(只是一个简单的 7 槽 int 数组),它将 tableview 要求的部分映射到底层的 fetchedresultscontroller 部分。每个数组槽都将具有适当的部分索引或“-1”,其中没有基础部分(并且应该显示“无预订”单元格)。

    所以,我的 refetchResults 方法变成了:

    - (void)refetchResults {    
    
        // Create the fetch request for the entity.
        NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
        NSEntityDescription *entity = [NSEntityDescription entityForName:@"Booking" inManagedObjectContext:self.managedObjectContext];
        [fetchRequest setEntity:entity];
    
        // Limit to this weeks data
        [fetchRequest setPredicate:
         [NSPredicate predicateWithFormat:@"(date >= %@) && (date <= %@) && (resource == %@)",
          firstDate,lastDate,resourceId]];
    
        // Edit the sort key as appropriate.
        NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"date" ascending:YES];
        NSSortDescriptor *sortDescriptor2 = [[NSSortDescriptor alloc] initWithKey:@"recId" ascending:YES];
        NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, sortDescriptor2, nil];
        [fetchRequest setSortDescriptors:sortDescriptors];
    
        // Set up FRC
        NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:@"date" cacheName:nil];
        self.fetchedResultsController = aFetchedResultsController;
        self.fetchedResultsController.delegate = self;
        [aFetchedResultsController release];
        [fetchRequest release];
        [sortDescriptor release];
        [sortDescriptor2 release];
        [sortDescriptors release];
    
        // Run up FRC
        NSError *error = nil;
        if (![fetchedResultsController_ performFetch:&error]) {
            NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
            abort();
        }
    
        // Update FRC map
        [self updateFRCMap];
    
        // Reload table
        [tableView reloadData];
    }
    

    映射设置如下。每当需要刷新映射时都会调用此方法 - 例如,当我从 fetchedresultscontroller 获取已添加/删除/等项目的回调时。

    - (void)updateFRCMap {
    
        // Set mapping table for seven days of week to appropriate section in frc
        for (int idx=0;idx<7;idx++) { frcMap[idx] = -1; }   // Reset mappings
        // For each section
        for (int sidx=0; sidx<[[self.fetchedResultsController sections] count]; sidx++) 
        {
            // If section has items
            if ([[[self.fetchedResultsController sections] objectAtIndex:sidx] numberOfObjects] > 0) 
            {
                // Look at first booking of section to get date
                NSDate *date = [(Booking *)[self.fetchedResultsController objectAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:sidx]] date];
                // Decide on array index by difference in firstDate and booking date
                int idx = (int)[date timeIntervalSinceDate:firstDate]/86400;
                // Set map
                frcMap[idx] = sidx;
            }
        }
    }
    

    这可能会被优化一点,但现在可以正常工作。我怀疑它可能会遇到需要修复的 GMT/BST 时钟更改问题......不是时钟更改问题都那么紧迫,嗯 Apple? ;P

    之后只是响应tableview时使用映射数组的一种情况:

    #pragma mark -
    #pragma mark Table view data source
    
    // Gets the booking from the fetchedResultsController using a remapped indexPath
    - (Booking *)bookingForMappedIndexPath:(NSIndexPath *)indexPath {
        return (Booking *)[self.fetchedResultsController objectAtIndexPath:
                           [NSIndexPath indexPathForRow:indexPath.row inSection:frcMap[indexPath.section]]];
    }
    
    - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
        return 7;   // 7 days viewed
    }
    
    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    
        // Rows in section or 1 if no section
        if (frcMap[section] != -1) {
            id <NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:frcMap[section]];
            return [sectionInfo numberOfObjects];
        } else {
            return 1;
        }
    
    }
    
    - (UITableViewCell *)tableView:(UITableView *)_tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    
        static NSString *CellIdentifier = @"RegularCell";
    
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
        if (cell == nil) {
            cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
        }
    
        // Configure the cell.
        [self configureCell:cell atIndexPath:indexPath];
        return cell;
    }
    
    - (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath {
    
        // If no actual bookings for section then its a blank cell
        if (frcMap[indexPath.section] == -1) {
    
            // Configure a blank cell.
            cell.textLabel.text = @"No Bookings";
            cell.detailTextLabel.text = @"";
    
            cell.textLabel.font = [UIFont systemFontOfSize:16];
            cell.textLabel.textColor = [UIColor lightGrayColor];
    
            cell.accessoryType = UITableViewCellAccessoryNone;
            cell.selectionStyle = UITableViewCellSelectionStyleNone;
    
        } else {
    
            // Regular cell
            Booking *booking = [self bookingForMappedIndexPath:indexPath];
            cell.textLabel.text = booking.desc;
            cell.detailTextLabel.text = [NSString stringWithFormat:@"%@ %@", booking.location, booking.detail];
    
            cell.textLabel.font = [UIFont systemFontOfSize:14];
            cell.textLabel.textColor = [UIColor darkTextColor];
            cell.detailTextLabel.font = [UIFont systemFontOfSize:12];
    
            cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
            cell.selectionStyle = UITableViewCellSelectionStyleBlue;
        }
    }
    

    非常欢迎任何 cmet 或更好的写作方式:)

    【讨论】:

    • 谢谢。在我正在开发的应用程序中,我遇到了完全相同的问题,试图在空白的部分显示“昨天、今天、明天”事件,并带有“今天没有”单元格。在我看到这个之前,我实际上采用了相同的解决方案。可惜 FetchedResultsController 对此没有任何支持...
    【解决方案2】:

    我没用过这么多,但你可以看看 NSFetchedResultsSectionInfo 协议。显然可以这样使用:

    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 
    {
    NSInteger numberOfRows = 0; 
    if ([[fetchedResultsController sections] count] > 0)
        {
        id <NSFetchedResultsSectionInfo> sectionInfo = [[fetchedResultsController sections] objectAtIndex:section];
        numberOfRows = [sectionInfo numberOfObjects];
        }
    return numberOfRows;
    }
    

    祝你好运。

    【讨论】:

    • 感谢您的回复。除非我遗漏了一些东西,否则我仍然需要做一个映射练习来找到合适的部分——即:tableView 会要求第 0 到 6 部分(例如,代表 11 月 1 日到 11 月 7 日),但 NSFetchedResultsController 可能只有第 0、1 和 2 部分(可能代表 11 月 2 日、11 月 3 日和 11 月 6 日的预订)。我可以在每次需要参考 Bookings 时进行此映射,或者在构造 NSFetchedResultsController 时缓存映射,但我希望有更好/更整洁的方法来做到这一点。
    • 你是对的。很抱歉不能提供更多帮助。如果时间允许,我稍后会尝试这个。很想知道你是否/如何解决这个问题。
    • 实际上,对此进行更多思考,我认为目前一个可用的解决方法是像往常一样创建一个 NSFetchedResultsController 但在最后设置一个“映射”数组的字段来翻译表格视图的部分到 NSFetchedResultsController 的部分。这应该意味着我可以从 resultsController 获取对象通知(这很好;) - 当通知通过 resultsController 添加/删除的项目时,我只需要更新“映射”数组。无论哪种方式,如果我找到更好的解决方案,我一定会在这里发布。
    • 听起来很有希望。我期待看到您的解决方案。
    【解决方案3】:

    我也有这个问题。我写了一个 NSFetchedResultsController 的子类来解决这个问题:

    https://github.com/timothyarmes/TAFetchedResultsController

    提姆

    【讨论】:

    • 感谢蒂姆的辛勤工作,但对您的解决方案的真实印象是,它不容易被放入现有项目中。创建一个新实体来支持这一点,并处理删除逻辑比我预期的“绕过这个小问题”要多得多
    猜你喜欢
    • 2012-03-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-30
    • 1970-01-01
    • 2019-03-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多