我在OS X开发中的早期步骤。
NSTreeview绑定到一个包含自定义类的各种代理对象的数组。其中一些对象绑定到我的Core Data Store,因此我的侧边栏包含多个不同NSManagedObject
个实体的列表。由于某种原因,moc实体是重复的,每个都列出两次。像这样:
NSTreeController绑定到NSArray,sidebarItems,这里填充:
- (void)populateOutlineContents
{
// hide the outline view - don't show it as we are building the content
[self.myOutlineView setHidden:YES];
BrowserItem *dashboardItem = [BrowserItem itemWithTitle:@"Home"];
dashboardItem.nodeIcon = [NSImage imageNamed:@"home"];
BrowserItem *todayItem = [BrowserItem itemWithTitle:@"Today"];
BrowserItem *thisWeekItem = [BrowserItem itemWithTitle:@"This Week"];
BrowserItem *separatorItem = [BrowserItem itemWithTitle:nil];
// Setup Projects
self.projectsSidebarGroup = [BrowserItem itemWithTitle:PROJECTS_NAME];
self.projectsSidebarGroup.childItemTitleKeypath = @"projectName";
[self.projectsSidebarGroup bindChildItemsToArrayKeypath:@"projectsArrayController.arrangedObjects" onObject:self];
// Setup Crewmen
self.crewmenSidebarGroup = [BrowserItem itemWithTitle:CREWMEN_NAME];
self.crewmenSidebarGroup.childItemTitleKeypath = @"fullName";
[self.crewmenSidebarGroup bindChildItemsToArrayKeypath:@"crewmenArrayController.arrangedObjects" onObject:self];
self.sidebarItems = @[dashboardItem, todayItem, thisWeekItem,separatorItem, self.projectsSidebarGroup, self.crewmenSidebarGroup];
[self.myOutlineView setHidden:NO]; // we are done populating the outline view content, show it again
}
在viewDidLoad中,我为每个组设置了Core Data绑定和NSArrayControllers:
[self populateOutlineContents];
NSPredicate *ActiveProjectsFilter = [NSPredicate predicateWithFormat:@"inactive == NO"];
NSSortDescriptor *sortByName = [[NSSortDescriptor alloc] initWithKey:@"projectName" ascending:YES];
self.projectsArrayController = [self controllerForEntity:@"Project" filter:ActiveProjectsFilter sortBy:@[sortByName]];
NSPredicate *activeCrewmenFilter = [NSPredicate predicateWithFormat:@"Active == YES"];
NSSortDescriptor *sortByLast = [[NSSortDescriptor alloc] initWithKey:@"nameLast" ascending:YES];
NSSortDescriptor *sortByFirts = [[NSSortDescriptor alloc] initWithKey:@"nameFirst" ascending:YES];
self.crewmenArrayController = [self controllerForEntity:@"WorkMan" filter:activeCrewmenFilter sortBy:@[sortByLast, sortByFirts]];
实际的控制器在这里设置:
-(NSArrayController *)controllerForEntity:(NSString *)aEntityName filter:(NSPredicate *)filter sortBy:(NSArray *)sortDescriptors
{
NSArrayController *controller = [[NSArrayController alloc] init];
controller.managedObjectContext = self.managedObjectContext;
controller.entityName = aEntityName;
controller.automaticallyRearrangesObjects = YES;
controller.automaticallyPreparesContent = YES;
if (filter) {
controller.fetchPredicate = filter;
}
if (sortDescriptors) {
controller.sortDescriptors = sortDescriptors;
}
NSError *error;
if (![controller fetchWithRequest:nil merge:NO error:&error])
{
ALog(@"Error fetching %@", aEntityName);
}
return controller;
}
我检查了我的NSArrayControllers的状态,arrangeObjects,那里只有预期的项目数。实际上没有重复项,因此OutlineView中的重复项来自哪里?
我的代理对象,如果它有帮助:
#import "BrowserItem.h"
@implementation BrowserItem
+(instancetype)itemWithTitle:(NSString *)title
{
BrowserItem *item = [[BrowserItem alloc] init];
[item setTitle:title];
return item;
}
- (instancetype)init
{
if (self = [super init])
{
self.children = [NSMutableArray array];
self.modelToItemMapping = [NSMapTable weakToStrongObjectsMapTable];
self.childItemsArrayController = [[NSArrayController alloc] init];
[self.childItemsArrayController addObserver:self forKeyPath:@"arrangedObjects" options:NSKeyValueObservingOptionPrior context:NULL];
}
return self;
}
- (void)bindChildItemsToArrayKeypath:(NSString*)keypath onObject:(id)object;
{
[self.childItemsArrayController bind:@"contentArray" toObject:object withKeyPath:keypath options:nil];
self.isGroup = YES;
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if ([keyPath isEqualToString:@"arrangedObjects"])
{
[self willChangeValueForKey:@"children"];
NSMutableArray *watchableChildren = [self mutableArrayValueForKey:@"children"];
[watchableChildren removeAllObjects];
for (id modelItem in self.childItemsArrayController.arrangedObjects)
{
BrowserItem *browserItem = [self.modelToItemMapping objectForKey:modelItem];
if (!browserItem)
{
browserItem = [BrowserItem itemWithTitle:[modelItem valueForKey:self.childItemTitleKeypath]];
[self.modelToItemMapping setObject:browserItem forKey:modelItem];
}
[watchableChildren addObject:browserItem];
}
[self didChangeValueForKey:@"children"];
}
}
@end
------------ UPDATE ---------
因此,我从NSMutableArrayForKey
更改为创建新NSMutableArray
并在我的对象全部设置后替换子数组。这消除了重复列表,但仍然存在一些问题。我在那里放了一些调试语句和计数器,我可以看到例程在每个组上运行3或4次。这似乎没必要。我想它不会重复,因为之前的结果现在被最新的运行所取代。仍然希望解决而不是保持原样。
这是修订后的观察员:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if ([keyPath isEqualToString:@"arrangedObjects"])
{
[self willChangeValueForKey:@"children"];
NSMutableArray *replacementChildren = [NSMutableArray array];
for (id modelItem in self.childItemsArrayController.arrangedObjects)
{
BrowserItem *browserItem = [self.modelToItemMapping objectForKey:modelItem];
if (!browserItem)
{
browserItem = [BrowserItem itemWithTitle:[modelItem valueForKey:self.childItemTitleKeypath]];
[self.modelToItemMapping setObject:browserItem forKey:modelItem];
[self.itemToModelMapping setObject:modelItem forKey:browserItem];
}
[replacementChildren addObject:browserItem];
}
_children = replacementChildren;
[self didChangeValueForKey:@"children"];
}
}
答案 0 :(得分:0)
重复的项目可能是由重复的更改通知引起的。 didChangeValueForKey:@"children"
和mutableArrayValueForKey
都通知观察者。这会使树控制器混淆。使用willChangeValueForKey
didChangeValueForKey
组合或使用mutableArrayValueForKey
。
observeValueForKeyPath
可以多次调用。添加或删除对象时,arrangedObjects
NSArrayController
更改,内容排序时以及NSArrayController
认为必须更改arrangedObjects
时。