質問

I have a simple pre-loaded SQLite DB and Core Data model with an Entity called “Scales” which includes a NSString “name” and a NSString field called “categories” (I decided not to use a relationship since this was a simple one-to-one without a large amt of data).

Current design is as follows using core data: UITableViewController of category [a to z] ->click on category -> DetailTableViewController displaying of all “names” [a to z] which fall under that “category”.

I am able to accomplish these tasks of drilling down using the standard Apple way of fetching and using a predicate to sort, however it does require adding another view controller to the stack.

My goal is to be able to use an expandable UItableview where the section names are the “categories”, and the cells are the “names”; thereby making it unnecessary to push a detail view onto the stack/make the app more aesthetically pleasing.

I’ve attempted to integrate the CollapseTableViewController from Tim Moose’s TLIndexPathTools, with my project, but I am still unclear with the details….most specifically, where to link up my managedObjectcontext and fetch specifiers so that his tools can do the rest.

役に立ちましたか?

解決

To integrate with Core Data, you should only need to replace TLCollapsibleTableViewController's default TLIndexPathController with one initialized with an NSFetchRequest:

- (void)viewDidLoad
{
    [super viewDidLoad];

    // replace default indexPathController with one configured with an NSFetchRequest
    NSManagedObjectContext *context = ...;// Your managed object context
    NSFetchRequest *fetch = ...; // Request that fetches Scales sorted by category
    self.indexPathController = [[TLIndexPathController alloc] initWithFetchRequest:fetch managedObjectContext:context sectionNameKeyPath:@"categories" identifierKeyPath:nil cacheName:nil];

    // perform initial fetch
    NSError *error;
    [self.indexPathController performFetch:error];
    if (error) {
        // handle error
    }
}

The rest of this answer explains how the Core Data integration works and highlights a cool feature of TLIndexPathControllerDelegate.

TLCollapsibleTableViewController uses TLCollapsibleDataModels as the data model. TLCollapsibleDataModels are constructed by providing a backing data model containing all possible rows and a set of expanded section names:

TLCollapsibleDataModel *dataModel = [[TLCollapsibleDataModel alloc] initWithBackingDataModel:backingDataModel expandedSectionNames:expandedSectionNames];

The resulting data model only contains the rows to be displayed (though the full data set is accessible through the backingDataModel property). What TLCollapsibleTableViewController does is to automatically update the data model when the user expands or collapses a section.

When you enable Core Data integration as described above, the TLIndexPathController will also automatically update the data model on the initial fetch result and on any fetch result changes. But this data model is the backing model and needs to be converted to TLCollapsibleDataModel. To make this happen automatically, I added the following implementation of controller:willUpdateDataModel:withDataModel:, which allows one to arbitrarily modify the new data model before the updates are generated:

- (TLIndexPathDataModel *)controller:(TLIndexPathController *)controller willUpdateDataModel:(TLIndexPathDataModel *)oldDataModel withDataModel:(TLIndexPathDataModel *)updatedDataModel
{
    // If `updatedDataModel` is already a `TLCollapsibleDataModel`, we don't need to do anything.
    if ([updatedDataModel isKindOfClass:[TLCollapsibleDataModel class]]) {
        return nil;
    }
    // Otherwise, we assume `updatedDataModel` is the backing model - maybe it came from a
    // Core Data fetch request or maybe it was provided by custom code - and we need to
    // constructe the `TLCollapsibleDataModel`.
    NSMutableSet *expandedSectionNames = nil;
    // If `oldDataModel` is a `TLCollapsibleDataModel`, we need to preserve the
    // expanded state of known sections.
    if ([oldDataModel isKindOfClass:[TLCollapsibleDataModel class]]) {
        expandedSectionNames = [NSMutableSet setWithSet:((TLCollapsibleDataModel *)oldDataModel).expandedSectionNames];
        // Now filter out any section names that are no longer present in `updatedDataModel`
        [expandedSectionNames intersectSet:[NSSet setWithArray:updatedDataModel.sectionNames]];
    }
    // Construct and return the `TLCollapsibleDataModel`
    TLCollapsibleDataModel *dataModel = [[TLCollapsibleDataModel alloc] initWithBackingDataModel:updatedDataModel expandedSectionNames:expandedSectionNames];
    return dataModel;
}

So if updatedDataModel isn't a TLCollapsibleDataModel, it is assumed to be the backing model and is converted to TLCollapsibleDataModel, preserving the expanded state captured in oldDataModel. This will not only work for NSFetchRequest generated updates but you can also set a backing model in your custom code self.indexPathController.items = ... or self.indexPathController.dataModel = ... if you have any reason to do so.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top