2
votes

I have a Core Data project with an NSTableView where the columns are bound to an NSArrayController. In turn, the controller's content is bound to the main managed object context of the AppDelegate.

I've sub-classed NSTextFieldCell's to provide a more customised way of displaying the data. Everything seems to work well but when I enumerate through the objects in the context and change an attribute, the NSArrayController doesn't seem to pass the new values through to the table. I know they're being changed as the sortDescriptors work and the table organises itself according to the new data but while still displaying the old values. I'm guessing this is down to the recycling of the NSCell's that display the data: although the underlying values have been changed in the Core Data DB they're not re-drawing.

After trying various KVO notifications with [myArrayController willChangeValueFor: aKeyPath] and the corresponding didChange as well as simply [myTable reloadData]. My only solution has been to unhook the managedObjectContext binding of the controller, and re-hook it after processing, like this:

self.hasBackgroundOp = YES;
self.isCancelled = NO; //can get changed with a 'cancel' button in the UI
    if (self.managedObjectContext.hasChanges) {
        [self. managedObjectContext save:nil];
    }
    [myArrayController setManagedObjectContext:nil]; //brutal un-hooking of controller
    dispatch_group_t dispatchGroup = dispatch_group_create();
    dispatch_group_async(dispatchGroup, dispatch_get_global_queue(0, 0), ^{
        .
        .
        // create a thread context, set the persistent store co-ord
        // and do some processing on the
        // NSManagedObjects that are fetched into an array just
        // for the purpose of this thread
        .
        .
        [ctx save:&error];
        //and after the processing is finished then refresh the table
        dispatch_async(dispatch_get_main_queue(), ^{
            [myArrayController setManagedObjectContext:self.managedObjectContext]; //this forces re-drawing of the table
            self.hasBackgroundOp = NO;
            self.isCancelled = NO;
        });
    });

This seems really brutal to be doing [myArrayController setManagedObjectContext:nil]; and then setting it again just to refresh the table's contents. I just can't believe I've done this correctly although it works just fine. Could anyone advise of a better way? Thanks!

UPDATE: actually setting the managed object context doesn't solve the problem. It seems to have stopped working. If the code is run twice in a row then the app just hangs. Comment out the -setManagedObjectContext: lines and the code can be run as many times as required.

In response to the comments, my custom cell uses the following to display itself:

NSInteger index = [[self stringValue] integerValue] + 1;
NSAttributedString *cellText = [[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@"%lu",index] attributes:attributes];
NSRect textRect;
textRect.origin = cellFrame.origin;
textRect.origin.x += (cellFrame.size.width - cellText.size.width) / 2;
textRect.origin.y += (cellFrame.size.height - cellText.size.height) / 2;
textRect.size.width = cellText.size.width;
textRect.size.height = cellText.size.height;
[cellText drawInRect:textRect];

The array controller is setup in the main nib and has it's managed object context bound to the AppDelegate's. There are no fetch requests or predicates in the nib - a sort descriptor is bound in the AppDelegate's code:

[myArrayController setSortDescriptors:[NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:kINDEX ascending:YES]]];

Grab of controller options

1
Add more information about your custom cell. If the array controller is updating its sorting based on inserted content, everything should "just work" with stock cells. If you find some other property displays properly, but not your custom cell, then that's where your problem is and you should document it more thoroughly here.Joshua Nozzi
I agree the problem is likely with your custom cell, but keep in mind that a table view will draw its cells again when the sort changes. You could try putting an NSLog in your cell's -drawRect: to confirm that's happening.paulmelnikow
Is your array controller also bound to a fetch request or filter predicate?paulmelnikow
[myArrayController setManagedObjectContext:nil]; might be helpful for my question. stackoverflow.com/questions/14049027/… ThanksCullen SUN
Thanks all. I don't think there is a problem with my custom cell as if I revert to the bog-standard cells then the non-refreshing persists. Putting an NSLog in the cell does show it being re-drawn so it's definitely not getting the updated info.Todd

1 Answers

1
votes

Think I've found it - I added the following to the end of the threaded processing and executed it on the main thread:

[self.managedObjectContext reset];
[myArrayController fetch:self];

Resetting the MOC apparently clears it so forcing the controller to redraw the cells. The -fetch appears to be necessary otherwise the table goes blank...