Я пытаюсь использовать функцию «Автосохранение расширенных элементов». Когда я расширяю группу с ее дочерними элементами и перезапускаю приложение, все дочерние элементы снова сворачиваются, и я не знаю, почему они не останутся расширенными. Я использую основные данные для хранения элементов исходного списка.

Вот что я сделал / установил до сих пор:

  • Проверено "Автосохранение расширенных элементов" в NSOutlineView (исходный список)
  • Задайте имя для "Автосохранение"
  • dataSource и делегировать выходы, назначенные моему контроллеру

Это моя реализация для 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 еще не завершил инициализацию. Есть идеи, как это решить?

7
krema 11 Сен 2014 в 18:03
2
Вы нашли решение? У меня аналогичная проблема, хотя я не использую CoreData и использую привязки. Действительно, метод outlineView: itemForPersistentObject: вызывается до того, как приложение завершит запуск.
 – 
onekiloparsec
10 Мар 2015 в 11:02

6 ответов

Лучший ответ

У меня была проблема, что моя реализация -outlineView: itemForPersistentObject: вообще не вызывалась. Оказывается, этот метод вызывается, когда установлено autosaveExpandedItems или autosaveName. Мое решение заключалось в том, чтобы установить оба свойства в Code, а НЕ в InterfaceBuilder. Когда я устанавливаю свойства после назначения делегата, вызывается метод.

3
Karsten 6 Мар 2016 в 18:08

Я заставил это работать - вам нужно вернуть соответствующий узел дерева вместо «просто» его представленного объекта.

В 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
MrMage 17 Июл 2015 в 12:47

Расширяя решение Карстена:

Метод -outlineView:itemForPersistentObject: вызывается после выполнения того, что предлагает Карстен, но ТОЛЬКО если вы также устанавливаете источник данных перед установкой делегата.

Поэтому, если ответ Карстена не работает, проверьте, где установлен ваш источник данных, и настройте его соответствующим образом.

(хотел написать это как комментарий, но мне не разрешено из-за моего статуса новичка ...)

1
Tamás Sengel 15 Сен 2017 в 11:20

У меня это никогда не работало.

Это мой нынешний способ сделать это:

Сначала я добавил атрибут isExpanded и сохранил для каждого узла статус в базе данных.

enter image description here

Во-вторых, я расширяю узлы, когда мой 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"];
    }
}
0
krema 11 Мар 2015 в 13:03
2
По моему скромному мнению, это очень плохой поступок. Добавление атрибута isExpanded к объекту модели просто смешивает модель и пользовательский интерфейс и нарушает шаблон модель-представление-контроллер. Что произойдет, если завтра вы решите изменить представление структуры на представление таблицы или коллекции? Этот атрибут становится бесполезным. Что произойдет, если вы решите, что ваши объекты можно отображать в двух разных схемах? Вы добавите атрибут? Кроме того, если вы сохраните свою модель в облаке, вы будете генерировать сетевой трафик каждый раз, когда ваш пользователь разворачивает / сворачивает элемент? Очень плохой...
 – 
AirXygène
23 Июн 2018 в 10:58

Ответ Swift 5

Карстен прав, 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
vomi 11 Ноя 2019 в 13:58
Как заставить это работать, если вы используете древовидный контроллер? Мне нужно установить ViewController в качестве источника данных, но затем он жалуется на все виды других недостающих функций, таких как numberOfChildrenOfItem и т. Д., Которые, предположительно, он получит от treeController
 – 
Duncan Groenewald
20 Май 2020 в 09:45
@DuncanGroenewald, я не использую контроллер дерева, у меня возникло слишком много проблем с ним. У меня больше гибкости при использовании источника данных и делегата.
 – 
vomi
5 Июл 2021 в 18:47
Выглядит аккуратно, но я нигде не могу найти managedObjectContext и persistentStoreCoordinator. Нашел NSManagedObjectContext и NSPersistentStoreCoordinator ... Не могли бы вы мне намекнуть?
 – 
RoyRao
15 Июл 2021 в 10:59

Ух ты! Спустя 6 лет это все еще вызывает головные боли.

Сначала я не мог заставить это работать, даже с полезным решением Карстена по настройке autoSaveName и autosaveExpandedItems в коде; itemForPersistentObject все еще вызывался до того, как был заполнен контурView. Решение для меня, хотя и не очень элегантное, заключалось в установке задержки 0,5 секунды перед установкой autosaveExpandedItems и autoSaveName. Задержка в полсекунды в моем приложении не заметна. Я тоже использовал код Воми. Делегат и источник данных устанавливаются в привязках 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
}
0
Pixelboy 5 Авг 2021 в 17:18