向 NSFetchedResultsController 添加额外的部分

发布于 2024-09-30 07:26:13 字数 2591 浏览 3 评论 0原文

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

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

我在此处获取了该应用程序的屏幕截图(抱歉,无法发布图像但因为我是 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 个部分,一个部分代表一周中的每一天,其中一些可能没有预订。

非常感谢任何帮助:)

I'm writing a small iPhone app for my company that shows bookings for each employee one week at a time. I'm using core data to get a list of 'Bookings' for a given week and want to display them in a UITableView broken down in to one section per day of the week.

The problem comes in that I need to show 7 sections for each day of the week (showing a 'No Bookings' cell where a section/date has no bookings).

I've got a screenshot of the app as it stands here (sorry can't post images yet as I'm new to StackOverlow)

At the moment I'm achieving this by using a 'fetchResults' method which gets the bookings and organises them in to an array of possible dates:

- (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];

}

My question is: is there any way to achieve the same result using NSFetchedResultsController? Somehow I'd need to get the NSFetchedResultsController to have 7 sections, one for each day of the week, some of them possibly having no bookings.

Any help much appreciated :)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

不喜欢何必死缠烂打 2024-10-07 07:26:13

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

这个想法是保存一个“映射”数组(只是一个简单的 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 时钟更改问题,需要修复......并不是说时钟更改问题那么紧急,是苹果吗? ;P

之后,这只是在响应表视图时使用映射数组的情况:

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

非常欢迎任何评论或更好的编写方式:)

So, being as the weather isn't very nice outside I've had a go at answering my own question and implementing the 'workaround' described in my reply to westsider.

The idea is to hold a 'mapping' array (just a simple 7 slot int array) which will map the section the tableview will ask for to the underlying fetchedresultscontroller section. Each array slot will have the appropriate section index or '-1' where there are no underlying sections (and where a 'No Booking' cell should be shown instead).

So, my refetchResults method becomes:

- (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];
}

The mapping is set in the following method. This is called whenever the mapping needs to be refreshed - for example when I get callbacks from the fetchedresultscontroller for items that have been added/deleted/etc.

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

This can probably be optimised a bit but works OK for now. I suspect it might suffer GMT/BST clock change problems which will need fixing ... not that clock change problems are all that urgent, eh Apple? ;P

After that it's just a case of using the mapping array when responding to the 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;
    }
}

Any comments or better ways of writing this are very much welcome :)

无声无音无过去 2024-10-07 07:26:13

我没用过这么多,但你可以查看 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;
}

祝你好运。

I haven't used this much, but you might check out NSFetchedResultsSectionInfo protocol. It can be used like this, apparently:

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

Good luck.

梦归所梦 2024-10-07 07:26:13

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

https://github.com/timothyarmes/TAFetchedResultsController

蒂姆

I had this problem too. I've written a subclass of NSFetchedResultsController to solve the issue:

https://github.com/timothyarmes/TAFetchedResultsController

Tim

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文