Re: Issue with -[NSOutlineView autosaveExpandedItems] - SOLVED
Re: Issue with -[NSOutlineView autosaveExpandedItems] - SOLVED
- Subject: Re: Issue with -[NSOutlineView autosaveExpandedItems] - SOLVED
- From: Bill Cheeseman <email@hidden>
- Date: Sat, 12 Jul 2014 16:50:04 -0400
On Jul 11, 2014, at 4:29 PM, Bill Cheeseman <email@hidden> wrote:
> And I've discovered another difficulty, although I think I can handle it. When you add, remove or edit a row that is a child of an expanded parent item, AppKit does not automatically call -outlineView:persistentObjectForItem: to update the autosaved data for the parent. Therefore, when you quit and relaunch the application, the new expanded parent item does not match the old expanded parent item autosaved in user defaults, and -outlineView:itemForPersistentObject: does not find a match. As a result, it returns nil and AppKit collapses the parent item on relaunch. The same thing will happen to an edited row if it was expanded before being edited. I think this issue is inherent in the autosave mechanism as Apple has implemented it.
>
....
> A third way to handle it would be to go the autosaved UUID route. The autosaved UUIDs would still be correct after quit and relaunch, and you could retrieve the revised items directly from the datasource in your implementation of the -outlineView:itemForPersistentObject: method. The datasource is always up to date by definition, and you would never encounter a mismatch.
>
> I fear the third approach might be the best. I hate to have to add a UUID to every item in the datasource, but Apple does provide a Cocoa UUID class to make it easy.
I have now added a UUID identifier field to each dictionary in my datasource array. Some of the dictionaries have nested arrays with still more dictionaries, and all of those dictionaries now also have UUID identifier fields. The nesting levels have no limit. The autosaveExpandedItems mechanism is now working perfectly.
In -outlineView:persistentObjectForItem:, I simply archive the UUID string.
The interesting method is -outlineView:itemForPersistentObject:. It unarchives the archived UUID string. Then it uses recursive blocks to search the data source array through all nested levels until it finds the item with the same UUID identifier and returns it. Here is the method:
- (id)outlineView:(NSOutlineView *)outlineView itemForPersistentObject:(id)object {
// Datasource method per NSOutlineViewDataSource formal protocol. Required if using autosaveExpandedItems. Called when application launches, once for each item in the autosaved array in the user defaults preferences file. AppKit calls this before -awakeFromNib, so the source list data source must be populated in the designated initializer.
NSString *identifier = [NSKeyedUnarchiver unarchiveObjectWithData:object];
__block id returnItem = nil;
__block void (^findItemForIdentifierInArray)(NSArray *) = ^(NSArray *contents) {
[contents enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
if ([[obj objectForKey:ID_KEY] isEqualToString:identifier]) {
returnItem = obj;
*stop = YES;
}
id subarray = [obj objectForKey:CONTENTS_KEY];
if (subarray) {
findItemForIdentifierInArray(subarray); // recursive
}
}];
};
findItemForIdentifierInArray([self sourceListContents]);
return returnItem;
}
The only remaining problem is that the recursive call to findItemForIdentifierInArray() is flagged with a warning that it is likely to lead to retain cycles, hence leaks. I don't know how to fix this, despite some hours of online searching. Can anybody with greater knowledge of blocks help me on this?
--
Bill Cheeseman - email@hidden
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden