2015-04-20 2 views
15

У меня есть NSOutlineView, который отображает иерархию каталогов. Он связан с NSTreeController, который связан с моим классом, который управляет узлами файловой системы. Когда происходит событие файловой системы, я запускаю уведомление KVO по ключевому пути children, что приводит к обновлению структуры. Но когда он обновляется, он неожиданно прокручивается до самого верха. Я хочу, чтобы положение прокрутки оставалось неизменным. Есть идеи?NSOutlineView подпрыгивает вверх, когда обновляется контент

Вот код, который выполняется, когда происходит событие FS:

- (void)URLWatcher:(CDEvents *)URLWatcher eventOccurred:(CDEvent *)event { 
    [self willChangeValueForKey:@"children"]; 
    children = nil; // this will refreshed next time children is called 
    [self didChangeValueForKey:@"children"]; 
} 

Это в модели, так что я не могу получить доступ мнения.

+0

Вы перезагружаете весь набросок или только отдельные элементы, которые необходимо обновить? – rocky

+0

@rocky Я не могу явно перезагрузить контурный вид. Контроллер дерева перезагружает его для меня. – tbodt

+0

Какой предмет получает это сообщение ?. Соответствующее представление узла FS или контроллер ?. Немного непонятно мне, почему вы стреляете в KVO здесь. Также как вы обновляете детей. Можете ли вы это показать. Я построил быстрый контроллер дерева/nsoutlineview и до тех пор, пока я не запускаю KVO, я не получаю сброс обновления при обновлении. –

ответ

0

Я могу посоветовать сохранить текущее смещение вида прокрутки и восстановить его после отправки уведомления о KVO и обновления плана.

- (void)updateOutlineView:(NSOutlineView *)outlineView 
{ 
    // first save offset 
    NSScrollView *scrollView = [outlineView enclosingScrollView]; 
    NSClipView *clipView = [scrollView contentView]; 
    NSPoint offset = clipView.bounds.origin; 
    // send KVO notification of the 'children' keypath 
    // ... 
    // restore offset 
    [clipView scrollPoint:offset]; 
} 

Посмотрите, что точка прокрутки является абсолютной величиной. Вы можете рассчитать точку назначения в соответствии с обновленной высотой обзора.

//... before the notification sent 
CGFloat height = [[[[scrollView documentView] frame] size] height]; 
CGFloat yValue = offset.y/height; 
//... after the outline view updated 
CGFloat newHeight = [[[[scrollView documentView] frame] size] height]; 
offset.y = newHeight * yValue; 
[clipView scrollPoint:offset]; 
+0

Это не сработает в моей ситуации. Чтобы уточнить, я добавил соответствующий код к моему вопросу. – tbodt

9

Я не тестировал и не пытался сделать следующее, но думал, что дам ему выстрел в любом случае.

Прежде всего, управлять чем-либо сложным с помощью NSTableView или NSOutlineView с помощью NS * Controller является болезненным и жертвует точным контролем в обмен на простоту. Если вы столкнулись с боевым поведением в этой ситуации, подумайте о внедрении протоколов данных и делегирования (NSTableViewDataSource, NSTableViewDelegate или NSOutlineViewDataSource, NSOutlineViewDelegate) в свой собственный пользовательский контроллер.

Во-вторых, комментарий Уоррена Бертона относительно увольнения уведомлений KVO уместен, так как вы должны сообщать ответственному диспетчеру (ваш NSTreeController) об изменениях, так как он контролирует (и наблюдает) эту коллекцию в любом случае. Более того, вы должны напрямую использовать методы добавления/вставки/удаления NSTreeController. То, как вы делаете это сейчас (уничтожая всю структуру каждый раз, когда вы сворачиваете, затем перезагружаете ее позже) приведет к перезагрузке всего дерева. Поскольку контроллер наблюдает за этой коллекцией, он сообщает обрисованному контуру, чтобы обновить себя, возможно, чтобы сначала увидеть пустой контур, затем более расширенную версию контура, которая потеряет состояния расширения вашего пользователя и т. Д. Изменение модель через контроллер дерева позволит использовать более интеллектуальные, более эффективные обновления.

В-третьих, вы могли бы рассмотреть возможность воспользоваться моей второй точки выше, подклассов NSTreeController и переопределение надстройку/вставки/удаления методы, чтобы сделать следующее:

  1. Спросите мнение обводки его -visibleRect.
  2. Позвоните супер, чтобы начать изменение.
  3. Рассказать набросок на -scrollRectToVisible:.

Возможно, вам придется отсрочить вызов в шаге 3, запланировав его по основному потоку (так происходит после текущей поездки через цикл цикла). Или, поочередно, замените шаг 3 с сохранением видимого прямоугольника где-то и внедряя методы NSOutlineViewDelegate -outlineView:didAdd/RemoveRowView:forRow:, чтобы проверить этот флаг. Затем вызовите -scrollRectToVisible:, если rect не равен нулю (не забудьте сбросить его в NSZeroRect, чтобы он не пытался отрегулируйте прокрутку каждый раз, когда строка контура добавляется или удаляется).

Неудобный, но разумный (?) Путь, который позволяет вам поддерживать NSTreeController.

В-четвертых, в качестве альтернативы (и как я мог бы пойти), вы можете полностью удалить NSTreeController и реализовать протоколы NSOutlineViewDataSource (и NSOutlineViewDelegate) в своем собственном классе контроллера и позволить этому контроллеру непосредственно обрабатывать добавление или удаление из вашей древовидной структуры , Затем он становится более чистым, так как вам не нужно беспокоиться о тайм-аутах KVO. При любом добавлении узлов вы можете заметить видимый прямоугольник, обновить представление схемы, а затем отрегулировать прокрутку в пределах одного и того же метода и пропустить цикл выполнения.

Надеюсь, это поможет и не слишком бессвязно. :-)

+0

Наконец, ответ! – tbodt

+0

Я решил попробовать четвертый вариант. Вероятно, награду щедрости. – tbodt