Cocoa 当内容更新时,NSOutlineView会跳到顶部

Cocoa 当内容更新时,NSOutlineView会跳到顶部,cocoa,nsoutlineview,nstreecontroller,Cocoa,Nsoutlineview,Nstreecontroller,我有一个显示目录层次结构的NSOutlineView。它绑定到一个NSTreeController,该控制器绑定到管理文件系统节点的类。当文件系统事件发生时,我会在子类键路径上触发一个KVO通知,这会导致大纲视图更新。但当它更新时,它会突然向上滚动到最顶端。我希望滚动位置保持不变。有什么想法吗 以下是发生FS事件时运行的代码: - (void)URLWatcher:(CDEvents *)URLWatcher eventOccurred:(CDEvent *)event { [self

我有一个显示目录层次结构的
NSOutlineView
。它绑定到一个
NSTreeController
,该控制器绑定到管理文件系统节点的类。当文件系统事件发生时,我会在
子类
键路径上触发一个KVO通知,这会导致大纲视图更新。但当它更新时,它会突然向上滚动到最顶端。我希望滚动位置保持不变。有什么想法吗

以下是发生FS事件时运行的代码:

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

这是在模型中,因此我无法访问视图。

我可以建议您保持当前滚动视图偏移量,并在发送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];

我还没有测试或尝试以下内容,但我想我还是会尝试一下

首先,使用NS*控制器管理NSTableView或NSOutlineView的任何复杂操作都是痛苦的,为了简单起见,需要牺牲精确的控制。如果在这种情况下发现自己的战斗行为,请考虑在您自己的自定义控制器中实现数据源和委托协议(NestLabVIEWDATA DATABOCE,NSTRAMEVIEW委托或NSOutLeNeVIEW数据源,NSOutLIVEVIEWDATATE)。 其次,Warren Burton关于触发KVO通知的评论是相关的,因为您应该将更改告知负责的控制器(您的NSTreeController),因为不管怎样,它是控制(并观察)该集合的控制器。更重要的是,您应该直接使用NSTreeController的add/insert/remove方法。你现在这样做的方式(每次清空整个结构,然后在以后重置)将导致整个树重新加载。由于控制器正在观察该集合,它告诉outline视图刷新自身,可能允许它先看到一个空的大纲,然后再看到大纲的进一步扩展版本,这将丢失用户的扩展状态等。通过树控制器修改模型将允许更智能的,更高效的视图更新

第三,您可以考虑利用我的第二点,通过子类化NSTreeController和重写ADD/INSER/DEVE方法来执行以下操作:

  • 向大纲视图询问其
    -visibleRect
  • 呼叫super以启动更改
  • 告诉大纲视图
    -scrollRectToVisible:
  • 在第3步中,您可能必须通过在主线程上调度调用来延迟调用(因此,在当前运行循环的行程之后发生)。或者,将步骤3替换为将可见rect存储在某处,并实现nsOutlineView:didAdd/RemoveRowView:forRow:方法来检查此标志,然后在rect非零的情况下从那里调用
    -scrollRectToVisible:
    (请记住将其重置为NSZeroRect,以便它不会在每次添加或删除轮廓行时都尝试调整滚动)

    笨重但合理的(?)路径,允许您保留NStree控制器

    第四,或者(按照我的方式),您可以完全删除NSTreeController并实现NSOutlineViewDataSource(和NSOutlineViewDelegate)在您自己的控制器类中使用协议,并让该控制器直接处理添加到树结构或从树结构中删除的操作。这样,它会变得更干净,因为您不必担心KVO计时问题。在任何节点的添加上,您都可以记下可见的rect,更新大纲视图,然后在同一方法内调整滚动,并遍历运行循环


    我希望这会有所帮助,而且不会太杂乱。:-

    您是重新加载整个大纲视图还是只加载需要更新的特定项目?@rocky我没有明确地重新加载大纲视图。树控制器为我重新加载它。什么对象获取此消息?。相关FS节点表示或控制器?。我不太清楚你为什么在这里发射KVO。还有,你如何刷新孩子们。你能给我看看吗。我构建了一个快速树控制器/nsoutlineview,只要我不启动KVO,更新时就不会重置视图。@WarrenBurton 1。我发布的代码在目录节点中。2.我启动KVO是为了告诉树控制器(它正在观察每个节点)子节点已经更新。如果你真的需要看我的代码,它在github上,在我的情况下是不起作用的。为了澄清,我在问题中添加了相关代码。我决定尝试第四个选项。很可能会赏金。