【问题标题】:Autosave Expanded Items of NSOutlineView doesn't work自动保存 NSOutlineView 的扩展项不起作用
【发布时间】:2014-09-11 14:03:53
【问题描述】:

我正在尝试使用“自动保存扩展项目”功能。当我用它的孩子展开一个组并重新启动应用程序时,所有孩子都再次折叠,我不知道为什么他们不会保持展开状态。 我正在使用核心数据来存储我的源列表项。

这是我到目前为止所做/设置的:

  • 在 NSOutlineView(源列表)中选中“自动保存展开的项目”
  • 为“自动保存”设置名称
  • 分配给我的控制器的数据源和委托出口

这是我对 outlineView:persistentObjectForItem 和 outlineView:itemForPersistentObject 的实现。

- (id)outlineView:(NSOutlineView *)anOutlineView itemForPersistentObject:(id)object
{
    NSURL *objectURI = [[NSURL alloc] initWithString:(NSString *)object];  
    NSManagedObjectID *mObjectID = [_persistentStoreCoordinator managedObjectIDForURIRepresentation:objectURI]; 
    NSManagedObject *item = [_managedObjectContext existingObjectWithID:mObjectID error:nil];
    return item;  
}

- (id)outlineView:(NSOutlineView *)anOutlineView persistentObjectForItem:(id)item
{
    NSManagedObject *object = [item representedObject];
    NSManagedObjectID *objectID = [object objectID];
    return [[objectID URIRepresentation] absoluteString];
}

有什么想法吗?谢谢。

编辑: 我有线索!问题可能是树控制器没有按时准备好它的内容。 applicationDidFinishLaunching、outlineView:persistentObjectForItem 等方法在数据加载之前执行,或者说 NSOutlineView 尚未完成初始化。任何想法如何解决这个问题?

【问题讨论】:

  • 您找到解决方案了吗?我有一个类似的问题,虽然我不使用 CoreData 并使用绑定。实际上,outlineView:itemForPersistentObject: 方法在应用程序完成启动之前被调用。

标签: objective-c nsoutlineview


【解决方案1】:

我遇到的问题是我的 -outlineView:itemForPersistentObject: 实现根本没有被调用。事实证明,当设置了“autosaveExpandedItems”或“autosaveName”时会调用此方法。 我的解决方案是在代码中而不是在 InterfaceBuilder 中设置这两个属性。当我在分配委托后设置属性时,会调用该方法。

【讨论】:

    【解决方案2】:

    我得到了这个工作 - 你需要返回相应的树节点,而不是“只是”它所代表的对象。

    itemForPersistentObject: 中,您需要return [self itemForObject:item inNodes:[_treeController.arrangedObjects childNodes]]; 而不是return item;

    - (id)itemForObject:(id)object inNodes:(NSArray *)nodes {
        for (NSTreeNode *node in nodes) {
            if ([node representedObject] == object)
                return node;
    
            id item = [self itemForObject:object inNodes:node.childNodes];
            if (item)
                return item;
        }
    
        return nil;
    }
    

    其中_treeController 是您用于填充大纲视图的NSTreeController 实例。

    【讨论】:

      【解决方案3】:

      扩展卡斯滕的解决方案:

      按照 Karsten 的建议,方法 -outlineView:itemForPersistentObject: 会被调用,但前提是您在设置委托之前还设置了数据源。

      因此,如果 Karsten 的回答似乎不起作用,请检查您的数据源的设置位置并进行相应调整。

      (想写这个作为评论,但由于我的新手身份,我不允许...)

      【讨论】:

        【解决方案4】:

        Swift 5 答案

        Karsten 是对的,itemForPersistentObject 必须返回一个 NSTreeNode。

        这是解决方案的 Swift 5 版本:

        // This method should return a NSTreeNode object
        func outlineView(_ outlineView: NSOutlineView, itemForPersistentObject object: Any) -> Any? {
            guard let uriAsString = object as? String,
            let uri = URL(string: uriAsString) else { return nil }
        
            if let psc = self.managedObjectContext.persistentStoreCoordinator,
                let moID = psc.managedObjectID(forURIRepresentation: uri),
                let group = self.managedObjectContext.object(with: moID) as? MyGroupEntity,
                let nodes = self.expensesTreeController.arrangedObjects.children {
                return self.findNode(for: group, in: nodes)
            }
            return nil
        }
        
        /// Utility method to find the corresponding NSTreeNode for a given represented object
        private func findNode(for object: NSManagedObject, in nodes: [NSTreeNode]) -> NSTreeNode? {
            for treeNode in nodes {
                if (treeNode.representedObject as? NSManagedObject) === object {
                    return treeNode
                }
            }
            return nil
        }
        

        【讨论】:

        • 如果您使用的是树形控制器,您如何使其工作?我必须将 ViewController 设置为数据源,但随后它抱怨各种其他缺少的功能,例如 numberOfChildrenOfItem 等,这大概会从 treeController 获得
        • @DuncanGroenewald,我不使用树控制器,我遇到了太多问题。我在使用数据源和委托时拥有更大的灵活性。
        • @vomi 看起来很整洁,但我似乎在任何地方都找不到 managedObjectContext persistentStoreCoordinator。找到了NSManagedObjectContextNSPersistentStoreCoordinator……你能给我一些提示吗?
        【解决方案5】:

        我从来没有得到这个工作。

        这是我目前的做法:

        首先,我添加了一个属性“isExpanded”,并将每个节点的状态保存在数据库中。

        其次,当我的 treeController 准备好其内容时,我会展开节点。

        - (void)applicationDidFinishLaunching:(NSNotification *)aNotification
        {  
            [treeSectionController addObserver:self
                             forKeyPath:@"content"
                                options:0
                                context:nil]; 
        }
        - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object     change:(NSDictionary *)change context:(void *)context
        {
            if (object == treeSectionController) {
                NSArray *sectionArray = [[treeSectionController arrangedObjects]     childNodes];
                for (NSTreeNode *node in sectionArray) {
                     if([[node representedObject] isExpandedValue]) {
                         [outlinePilesView expandItem:node];
                     }
                }
                [treeSectionController removeObserver:self forKeyPath:@"content"];
            }
        }
        

        【讨论】:

        • 在我看来,这是一件非常糟糕的事情。将 isExpanded 属性添加到模型对象只会混合模型和用户界面,并打破模型-视图-控制器模式。如果明天,您决定将大纲视图更改为表格或集合视图,会发生什么?该属性变得无用。如果您决定可以通过 2 个不同的轮廓视图显示您的对象,会发生什么?你会添加一个属性吗?此外,如果您将模型保存到云中,每次用户展开/折叠项目时都会产生网络流量?非常糟糕...
        【解决方案6】:

        哇! 6 年后,这仍然令人头疼。

        我最初无法让这个工作,即使 Karsten 的有用解决方案在代码中重新设置 autoSaveName 和 autosaveExpandedItems; itemForPersistentObject 在填充大纲视图之前仍被调用。对我来说,虽然不是很优雅的解决方案是在设置 autosaveExpandedItems 和 autoSaveName 之前设置 0.5 秒的延迟。我的应用程序中的半秒延迟并不明显。我也使用了 Vomi 的代码。 Delegate 和 dataSource 在 IB 绑定中设置。这是完整的解决方案:

        override func viewDidLoad() {
            super.viewDidLoad()
        
            let _ = Timer.scheduledTimer(withTimeInterval: 0.5, repeats: false) { (timer) in
                self.keywordsOutlineView.autosaveExpandedItems = true
                self.keywordsOutlineView.autosaveName = "KeywordsOutlineView"
                timer.invalidate()
            }
        
        }
        
        func outlineView(_ outlineView: NSOutlineView, persistentObjectForItem item: Any?) -> Any? {
            
            if let node = item as? NSTreeNode {
                if let object = node.representedObject as? FTKeyword {
                    return object.objectID.uriRepresentation().absoluteString
                }
            }
            return nil
        }
        
        // This method should return a NSTreeNode object
        func outlineView(_ outlineView: NSOutlineView, itemForPersistentObject object: Any) -> Any? {
            
            if outlineView == keywordsOutlineView {
                
                guard let uriAsString = object as? String,
                    let uri = URL(string: uriAsString) else { return nil }
                
                    if let psc = self.managedObjectContext.persistentStoreCoordinator,
                        let moID = psc.managedObjectID(forURIRepresentation: uri),
                        let group = self.managedObjectContext.object(with: moID) as? FTKeyword,
                        let nodes = self.keywordsTreeController.arrangedObjects.children {
                        
                        return self.findNode(for: group, in: nodes)
                    }
                    return nil
                
        
            }
            return nil
        }
        
        /// Utility method to find the corresponding NSTreeNode for a given represented object
        private func findNode(for object: NSManagedObject, in nodes: [NSTreeNode]) -> NSTreeNode? {
            
            for treeNode in nodes {
                if (treeNode.representedObject as? NSManagedObject) === object {
                    return treeNode
                }
            }
            return nil
        }
        

        【讨论】:

          猜你喜欢
          • 2014-05-09
          • 1970-01-01
          • 2022-11-09
          • 1970-01-01
          • 1970-01-01
          • 2021-07-09
          • 2017-11-17
          • 2018-04-19
          相关资源
          最近更新 更多