Skip to content

KVO controller for NSSet that behaves like an NSFetchedResultController without Core Data.

License

Notifications You must be signed in to change notification settings

samuelwford/CMSetController

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

13 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

CMSetController

CMSetController is a KVO controller for NSSet that behaves like an NSFetchedResultController without Core Data.

Given an object with an NSSet property containing objects you want to bind to a table view, provide (very similar to an NSFetchedResultController):

  • The key path of the set.
  • One or more key paths of properties to observe for the objects in the set.
  • One or more sort descriptors to order the objects.
  • Optionally the key path to a property of the objects to group them by.

The controller will provide a set of sections and objects to populate a UITableView datasource. It will also call back a delegate class when changes occur to the objects in the set for update, insertion, removal, reordering, and section changes.

Usage

Configure the CMSetController when configuring the view for the bound item. If you don't have a model object that holds a set, make the set a property of your controller.

// watch for changes to either the "name" or "favoriteColor" properties
NSArray *paths = @[@"name", @"favoriteColor"];

// sort by "favoriteColor" (first sort must match section key path, if any) then "name"
NSArray *sorts = @[[NSSortDescriptor sortDescriptorWithKey:@"favoriteColor" ascending:YES], [NSSortDescriptor sortDescriptorWithKey:@"name" ascending:YES]];

// observe the "friends" set property of the "self.document" object using the key paths and
// sorts setup above, group by "favoriteColor" into sections, and receive delegate callbacks
// as the set changes
_setController = [[CMSetController alloc] initWithObserved:self.document
                                                setKeyPath:@"friends"
                                                  keyPaths:paths
                                        sectionNameKeyPath:@"favoriteColor"
                                           sortDescriptors:sorts
                                                  delegate:self];
        
NSError *error;
if (![_setController performQuery:&error]) {
    NSLog(@"oops - %@", error);
    abort();
}
        
[self.tableView reloadData];

Populate the table view just like using an NSFetchedResultController:

#pragma mark - Table view

- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath
{
    CMFriend *friend = [[[[_setController sections] objectAtIndex:indexPath.section] objects] objectAtIndex:indexPath.row];
    
    cell.textLabel.text = friend.name;
    cell.detailTextLabel.text = friend.favoriteColor;
}

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return [[_setController sections] count];
}

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
    return [[[_setController sections] objectAtIndex:section] name];
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return [[[_setController sections] objectAtIndex:section] numberOfObjects];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    
    [self configureCell:cell atIndexPath:indexPath];
    
    return cell;
}

If you've implemented the CMSetControllerDelegate, you will get callbacks tailored to update the table view:

#pragma mark - Query Delegate

- (void)controllerWillChangeContent:(CMSetController *)controller
{
    [self.tableView beginUpdates];
}

- (void)controller:(CMSetController *)controller didChangeSection:(id<CMSetControllerSectionInfo>)sectionInfo atIndex:(NSUInteger)index forChangeType:(CMSetControllerChangeType)type
{
    NSIndexSet *indexSet = [NSIndexSet indexSetWithIndex:index];
    
    if (type == CMSetControllerChangeInsert)
        [self.tableView insertSections:indexSet withRowAnimation:UITableViewRowAnimationAutomatic];
    else
        [self.tableView deleteSections:indexSet withRowAnimation:UITableViewRowAnimationAutomatic];
}

- (void)controller:(CMSetController *)controller didChangeObject:(id)object atIndexPath:(NSIndexPath *)indexPath forChangeType:(CMSetControllerChangeType)type newIndexPath:(NSIndexPath *)newIndexPath
{
    switch (type) {
        case CMSetControllerChangeInsert:
            [self.tableView insertRowsAtIndexPaths:@[newIndexPath]
                                  withRowAnimation:UITableViewRowAnimationAutomatic];
            break;
            
        case CMSetControllerChangeDelete:
            [self.tableView deleteRowsAtIndexPaths:@[indexPath]
                                  withRowAnimation:UITableViewRowAnimationAutomatic];
            break;
            
            case CMSetControllerChangeUpdate:
                [self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
                break;
            
        case CMSetControllerChangeMove:
            [self.tableView deleteRowsAtIndexPaths:@[indexPath]
                                  withRowAnimation:UITableViewRowAnimationFade];
            [self.tableView insertRowsAtIndexPaths:@[newIndexPath]
                                  withRowAnimation:UITableViewRowAnimationFade];
            break;
            
        default:
            break;
    }
}

- (void)controllerDidChangeContent:(CMSetController *)controller
{
    [self.tableView endUpdates];
}

Contact

Samuel Ford @causticmango

License

CMSetController is released under the MIT license. See LICENSE file for more details.

About

KVO controller for NSSet that behaves like an NSFetchedResultController without Core Data.

Resources

License

Stars

Watchers

Forks

Packages

No packages published