保持 NSScrollView 固定

Posted

技术标签:

【中文标题】保持 NSScrollView 固定【英文标题】:Keep NSScrollView pinned down 【发布时间】:2012-09-06 08:20:56 【问题描述】:

我在 NSScrollView 中有一个 NSTableView。 NSTableView 的内容是通过绑定到绑定到 CoreData ManagedObjectContext 的 NSArrayController 来提供的。 NSTableView 的新行只是添加到并且永远不会删除或重新排序。

我希望将 NSTableView(或分别称为 NSScrollView)固定在底部,以便添加到表格中的新行立即可见。此外,这只会发生在 NSTableView 滚动到底部而不是当用户向上滚动以查看其他行时。

我目前正在使用以下代码

- (void)tableView:(NSTableView *)tableView didAddRowView:(NSTableRowView *)rowView forRow:(NSInteger)row 
    NSInteger numberOfRows = [tableView numberOfRows];
    NSLog(@"row: %lu, %lu numberOfRows ", row, numberOfRows);
    if (row == numberOfRows-1) 
        [tableView scrollRowToVisible:numberOfRows - 1];
    
 

到目前为止,这有效,但仅在窗口处于活动状态时。当窗口处于非活动状态时,不会调用 didAddRowView 方法,直到窗口再次变为活动状态。因此 NSTableView 不会立即向下滚动。由于 NSTableView 也通过 CoreData 绑定填充,我不知道在插入新项目后我可以手动触发滚动的任何一点。此外,didAddRowView 委托方法经常被调用(例如,当手动滚动表格时)并且只有在插入新项目后才真正需要向下滚动,这使得解决方案更加不优雅。

当应用程序处于活动状态和非活动状态时,我是否可以直接挂接到插入过程或 NSScrollView 以向下滚动到底部?

编辑: 为了完成或如果@rdelmar 的解决方案对您不起作用,我将有问题的 NSTableView 子类化并在 reloadData 方法中实现了这个

- (void) reloadData 
    [super reloadData];
    NSInteger numberOfRows = [self numberOfRows];
    NSRange rowsInRect = [self rowsInRect:self.superview.bounds];
    NSInteger lastVisibleRow = rowsInRect.location + rowsInRect.length;

    if (lastVisibleRow == numberOfRows-1) 
        [self scrollRowToVisible:numberOfRows - 1];
    

一旦在 ArrayController 中接收到新项目并应将其添加到表中,就会调用 reloadData 方法。在调用超类的 reloadData 之后,这会检查 TableView 是否滚动到底部(TableView 的最后一行是在 superview 中可见的最后一行 - 因此是周围的 NSScrollView)。如果是这样,它会向下滚动。如果没有,用户可能会向上滚动以查看表格的某些内容,并且不希望它自动向下滚动。

【问题讨论】:

您如何将新数据添加到您的核心数据数据库中?它是在用户操作下,还是通过服务器下载或类似的方式自动更新。 【参考方案1】:

我认为你应该可以使用 NSManagedObjectContext 通知,NSManagedObjectContextObjectsDidChangeNotification。创建一个自定义 NSTableView 类并为该通知添加一个观察者:

@implementation 自定义表格

-(void)awakeFromNib 
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(scrollTable:) name:NSManagedObjectContextObjectsDidChangeNotification object:nil];


-(void)scrollTable:(NSNotification *) aNote 
    if ([[aNote.userInfo allKeys] containsObject:@"inserted"]) 
        [self scrollRowToVisible:self.numberOfRows -1];
    else
        NSLog(@"It was not an insertion");
    

【讨论】:

问题在于 ArrayController 的 addObject(或任何其他添加/插入方法)没有被调用,因为它直接绑定到 CoreData ManagedObjectContext。否则我猜你的解决方案将是正确的选择 您如何向核心数据添加新数据? 行得通,谢谢。虽然,当您有很多此类表时,我发现了一种可能更适合的不同方法。它涉及在 TableView 的 reloadData 方法中挂钩更新。在我的测试过程中,这被调用的频率较低,我不确定在某些情况下滚动 NSManagedObjectContextObjectsDidChangeNotification 是否会在 TableView 应该滚动但尚未插入新项目的情况下不会提供计时问题。您的解决方案似乎有效,我接受它作为答案。或者,我已经用我的想法更新了我的问题。

以上是关于保持 NSScrollView 固定的主要内容,如果未能解决你的问题,请参考以下文章

NSScrollView 混乱

NSScrollView 中的自动布局约束

NSCollectionView+NSScrollView 中的鼠标悬停

如何在 NSScrollView 中强制重绘内容

NSScrollview 不以编程方式滚动?

将 NSImageView 添加到 NSScrollView