NSOutlineView 的自动保存扩展项不起作用

问题描述 投票:0回答:8

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

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

  • 在 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 尚未完成初始化。有什么想法如何解决这个问题吗?

objective-c nsoutlineview
8个回答
4
投票

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


2
投票

我让它工作 - 你需要返回相应的树节点,而不是“仅仅”它所代表的对象。

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

- (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
实例。


2
投票

扩展 Karsten 的解决方案:

方法

-outlineView:itemForPersistentObject:
在执行 Karsten 建议的操作后被调用,但前提是您在设置委托之前还设置了数据源。

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

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


2
投票

哇! 6年过去了,这仍然令人头痛。

即使使用 Karsten 的有用解决方案在代码中重新设置 autoSaveName 和 autosaveExpandedItems,我最初也无法使其正常工作;在填充outlineView 之前,itemForPersistentObject 仍在被调用。对我来说,解决方案虽然不是很优雅,但在设置 autosaveExpandedItems 和 autoSaveName 之前设置 0.5 秒的延迟。我的应用程序中的半秒延迟并不明显。我也使用了Vomi的代码。委托和数据源在 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
}

1
投票

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
}

1
投票

我通过在 Interface Builder 中设置

autosaveName
并在
autosaveExpandedItems
中的代码中设置
viewDidAppear
解决了这个问题:

override func viewDidAppear() {
    super.viewDidAppear()
    outlineView.autosaveExpandedItems = true
}

对于我的简单情况,当

item
只是
[Int]
时,这些
persistentObjectForItem:
itemForPersistentObject
起作用(我没有阅读返回
NSTreeNode
):

func outlineView(_ outlineView: NSOutlineView, persistentObjectForItem item: Any?) -> Any? {
    return item
}

func outlineView(_ outlineView: NSOutlineView, itemForPersistentObject object: Any) -> Any? {
    return object
}

0
投票

我从来没有成功过。

这是我目前的做法:

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

enter image description here

其次,当我的树控制器准备好其内容时,我展开节点。

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

0
投票

等待NSTreeController的arrangedObjects被初始化。

var treeContentObserver: NSKeyValueObservation?

override func viewDidLoad() {
    super.viewDidLoad()
    // setup outlineview, treeController, bind content, etc.
    treeContentObserver = treeController.observe(\.arrangedObjects, options: [.initial]) { (_, _) in
        Task {
            await MainActor.run {
                self.outlineView.autosaveExpandedItems = true
                self.outlineView.autosaveName = "uniqueNameForThisOutlineView"
                if let observer = self.treeContentObserver {
                    observer.invalidate()
                    self.treeContentObserver = nil
                }
            }
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.