NSTreeview复制我的对象

时间:2016-02-14 23:51:17

标签: objective-c macos nsoutlineview nstreecontroller

我在OS X开发中的早期步骤。

NSTreeview绑定到一个包含自定义类的各种代理对象的数组。其中一些对象绑定到我的Core Data Store,因此我的侧边栏包含多个不同NSManagedObject个实体的列表。由于某种原因,moc实体是重复的,每个都列出两次。像这样:

Groups bound to NSManangedObjects are repeated in list

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"];
    }
}

1 个答案:

答案 0 :(得分:0)

重复的项目可能是由重复的更改通知引起的。 didChangeValueForKey:@"children"mutableArrayValueForKey都通知观察者。这会使树控制器混淆。使用willChangeValueForKey didChangeValueForKey组合或使用mutableArrayValueForKey

observeValueForKeyPath可以多次调用。添加或删除对象时,arrangedObjects NSArrayController更改,内容排序时以及NSArrayController认为必须更改arrangedObjects时。