如何根据 Core Data 中的关系对象创建 UITableView 部分

Posted

技术标签:

【中文标题】如何根据 Core Data 中的关系对象创建 UITableView 部分【英文标题】:How do I create UITableView sections based on a relationship object in Core Data 【发布时间】:2014-01-18 12:11:11 【问题描述】:

当视图加载时,我将以下子类托管对象加载到数组中。

@class Aircraft;

@interface AircraftTypes : NSManagedObject

@property (nonatomic, retain) NSNumber * isIFRCapable;
@property (nonatomic, retain) NSString * model;
@property (nonatomic, retain) NSString * name;
@property (nonatomic, retain) NSNumber * numEngines;
@property (nonatomic, retain) NSString * type;
@property (nonatomic, retain) Aircraft *tailNumber;
@property (nonatomic, retain) NSNumber *sortOrder;
@end

@class AircraftTypes;

@interface Aircraft : NSManagedObject

@property (nonatomic, retain) NSString * homeStation;
@property (nonatomic, retain) NSNumber * isSimulator;
@property (nonatomic, retain) NSString * owner;
@property (nonatomic, retain) NSString * tailNumber;
@property (nonatomic, retain) AircraftTypes *aircraftType;
@property (nonatomic, retain) NSManagedObject *loggedFlights;

@end

关系是

AircraftTypes <--->> Aircraft

我可以并且已经成功地使用 UITableView 单元格中的关系中的子对象并继续编辑它们。我想要做的是弄清楚如何根据 AircraftType 成功创建 UITableView 部分,并使用属于该类型的飞机填充每个部分,并根据删除的行成功引用原始 Array(index)。这是我到目前为止所拥有的,但我被卡住了。

获取数组:

- (void)viewWillAppear:(BOOL)animated  
    [super viewWillAppear:animated];

self.managedObjectContext = [(ATPAppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
managedObjectContext = self.managedObjectContext;

self.aircraftList = [CoreDataHelper getObjectsForEntity:@"Aircraft" withSortKey:@"tailNumber" andSortAscending:YES andContext:self.managedObjectContext];
self.aircraftTypeList = [CoreDataHelper getObjectsForEntity:@"AircraftTypes" withSortKey:@"sortOrder" andSortAscending:YES andContext:self.managedObjectContext];   

加载数据(到目前为止,整个块都有效):

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView

return [aircraftTypeList count];


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

    // return the title of an individual category
AircraftTypes *thisType = [self.aircraftTypeList objectAtIndex:section];

return [NSString stringWithFormat:@"%@ %@", thisType.type, thisType.model];


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


AircraftTypes *thisType = [aircraftTypeList objectAtIndex:section];
int count = 0;

for (Aircraft *thisAircraft in self.aircraftList) 
    if (thisAircraft.aircraftType == thisType) 
        count++;
    


if (self.editing)  //Add insert row
    count++;


return count;


这个导致错误,我确实将一些数据预加载到堆栈中并在 applicationDidFinishLaunchingWithOptions 期间成功保存

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

static NSString *cellIdentifier = @"AircraftCell";
ATPSettingsMyAircraftTableCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];

if (cell == Nil) 
    cell = [[ATPSettingsMyAircraftTableCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier: cellIdentifier];


AircraftTypes *thisType = [aircraftTypeList objectAtIndex:indexPath.section];

cell.labelTailNumber.text = thisType.tailNumber.tailNumber;
cell.labelHomeStation.text = thisType.tailNumber.homeStation;
cell.labelAircraftModel.text = [NSString stringWithFormat:@"%@ %@",
                                thisType.type, thisType.model];

cell.labelHours.text = @"";
    //calcuate aircraft hours later

return cell;

而且我什至不知道如何为 commitEditingStyle 引用正确的对象。这是来自另一个 TableViewController 的 sn-p,它不使用部分,并且有一个引用我的 managedObject 的单个数组,所以我需要知道需要对此进行哪些更改才能引用正确的对象,因为我将要处理使用 indexPath.section 和 .row:

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

if (editingStyle == UITableViewCellEditingStyleDelete) 

        // Ensure that if the user is editing a name field then the change is committed before deleting a row -- this ensures that changes are made to the correct event object.
    [tableView endEditing:YES];

        // Delete the managed object at the given index path.
    NSManagedObject *flightConditionList = (self.flightConditionList)[indexPath.row];
    [self.managedObjectContext deleteObject:flightConditionToDelete];

        // Update the array and table view.
    [self.flightConditionList removeObjectAtIndex:indexPath.row];
    [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];

        // Commit the change.
    NSError *error;
    if (![self.managedObjectContext save:&error]) 
            // Replace this implementation with code to handle the error appropriately.
            // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
        abort();
    


如果飞行员能这么简单地做到这一点,请提前感谢。

这是一个编辑,尝试使用第一个答案。不确定我是否正确地将实体分成 AircraftTypes 实体上的部分,但我还需要根据 aircraftTypes.type 和 .model 命名部分标题。

用下面的代码试试。不确定 sectionNameKeyPath 是否针对类型为飞机类型的关系实体进行了正确编码。另外,我需要从飞机类型实体类型和模型的两个属性中标记部分标题。

- (NSFetchedResultsController *) fetchedResultsController 
if (fetchedResultsController != nil) 
    return fetchedResultsController;


NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Aircraft" inManagedObjectContext:self.managedObjectContext];

[fetchRequest setEntity:entity];

NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc]
                                    initWithKey:@"tailNumber" ascending:YES];
[fetchRequest setSortDescriptors:[NSArray arrayWithObject:sortDescriptor]];

[fetchRequest setFetchBatchSize:20];

NSFetchedResultsController *theController = [[NSFetchedResultsController alloc]
                                             initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:@"aircraftType" cacheName:@"Master"];

self.fetchedResultsController = theController;

fetchedResultsController.delegate = self;

return fetchedResultsController;

【问题讨论】:

【参考方案1】:

简短的回答是使用NSFetchedResultsController

NSFetchedResultsController 是使用表格的正确选择。特别是,它可以使用sectionNameKeyPath 创建,允许您控制部分。

这里的值sectionNameKeyPath 可以是你的aircraftType

更新 1

要查看NSFetchedResultsController 的工作示例,您可以查看NSFetchedResultsController Class Reference、sample code by Apple 和Core Data Tutorial for ios: How To Use NSFetchedResultsController。

显然,NSFetchedResultsController 是处理表格的正确方法,因为

它允许使用部分 它允许使用批量大小延迟加载数据,因此内存占用少 它允许对注册实体的修改(添加、删除、删除)做出反应 等

请注意,当您使用 NSFetchedResultsController 和部分时,您需要牢记以下几点

如果控制器生成节,则第一个排序描述符在 数组用于将对象分组;它的钥匙必须要么 与 sectionNameKeyPath 或使用其的相对排序相同 键必须使用 sectionNameKeyPath 匹配。

【讨论】:

见上面我编辑的代码(底部 sn-p)来实现 fetchedResultsController。我想我意识到我需要 sectionNameKeyPath 去飞机类型.type(NSString)。但是,我有多个相同“类型”的条目,因此是附加的“模型”标识符。您可以对多个关系字符串值执行 sectionNameKeyPath 吗?即sectionNameKeyPath:@"aircraftType.type+aircraftType.model"??【参考方案2】:

所以我能够使用您的方法和 fetchedResultsController 来解决这个问题,但是,它需要我重新设计数据模型以将飞机的类型和模型组合到一个对象中。 (例如汽车的品牌和型号)。它有效,但如果你能想出一种方法让它与它们作为单独的对象一起工作,那就太好了。不过感谢您的帮助!

【讨论】:

以上是关于如何根据 Core Data 中的关系对象创建 UITableView 部分的主要内容,如果未能解决你的问题,请参考以下文章

如何获取 Core Data 中的关系项属性?

如何根据相关对象集合的属性对 Core Data 结果进行排序?

Core Data - 如何在不同的上下文中建立两个对象之间的关系

在 Core Data 中为实体添加属性

如何管理 Core Data 中的一对多关系?

如何管理 Core Data 中的一对多关系?