【问题标题】:How to scroll to row added to NSTableView after animation has finished动画完成后如何滚动到添加到 NSTableView 的行
【发布时间】:2014-01-10 16:24:35
【问题描述】:

向 NSTableView 添加新行后,我想滚动到它。

当该行被添加到表的末尾时,滚动只会滚动到之前是最后一行的行。我最初认为我必须等待动画完成,但这并没有解决我的问题。这是我的代码:

        [NSAnimationContext beginGrouping];
        [_tableView insertRowsAtIndexes:indexSet withAnimation:NSTableViewAnimationEffectGap];
        [[NSAnimationContext currentContext] setCompletionHandler:^{

            // Scroll to the first inserted row

            NSUInteger firstIndex = [indexSet firstIndex];
            [_tableView scrollRowToVisible:firstIndex];

        }];
        [NSAnimationContext endGrouping];

我该怎么做?

【问题讨论】:

    标签: macos cocoa core-animation nstableview


    【解决方案1】:

    我找到了一个令我满意的解决方案:

    [_tableView insertRowsAtIndexes:indexSet withAnimation:NSTableViewAnimationEffectGap];
    
    [[NSOperationQueue mainQueue] addOperationWithBlock:^{
        NSUInteger firstIndex = [indexSet firstIndex];
        [_tableView scrollRowToVisible:firstIndex];
    }];
    

    我只是将滚动请求延迟到下一个运行循环。

    【讨论】:

    • 在最后插入一行后,我尝试使用它滚动到 tableview 的底部,它只是滚动到倒数第二行。有什么想法吗?
    【解决方案2】:

    我们遇到了这个问题,所以我们最终在其他动画发生时进行滚动,以保持行在屏幕上。你可以在你的动画分组中调用这段代码,你可以在其中进行 tableView 修改。

    代码如下所示:

    - (BOOL)scrollRowToVisible:(NSInteger)row animate:(BOOL)animate;
    {
        LIClipView *const clipView = (id)_sourceListOutlineView.enclosingScrollView.contentView;
        const NSRect finalFrameOfRow = [_sourceListOutlineView rectOfRow:row];
        const NSRect clipViewBounds = clipView.bounds;
    
        if (NSIsEmptyRect(finalFrameOfRow) || _sourceListOutlineView.numberOfRows <= 1)
            return NO;
    
        const NSRect finalFrameOfLastRow = [_sourceListOutlineView rectOfRow:(_sourceListOutlineView.numberOfRows - 1)];
        if (NSMaxY(finalFrameOfLastRow) <= NSHeight(clipViewBounds))
            // The source list is shrinking to fully fit in its clip view (though it might still be larger while animating); no scrolling is needed.
            return NO;
    
        if (NSMinY(finalFrameOfRow) < NSMinY(clipViewBounds)) {
            // Scroll top of clipView up to top of row
            [clipView scrollToPoint:(NSPoint){0, NSMinY(finalFrameOfRow)} animate:animate];
            return YES;
        }
    
        if (NSMaxY(finalFrameOfRow) > NSMaxY(clipViewBounds)) {
            // Scroll bottom of clipView down to bottom of source, but not such that the top goes off-screen (i.e. repeated calls won't keep scrolling if the row is higher than visibleRect)
            [clipView scrollToPoint:(NSPoint){0, MIN(NSMinY(finalFrameOfRow), NSMaxY(finalFrameOfRow) - NSHeight(clipViewBounds))} animate:animate];
            return YES;
        }
    
        return NO;
    }
    

    【讨论】:

    • 你好,威尔。在我看来,问题源于这样一个事实,即在执行插入之前行不存在。我尝试将我的 scrollToRow 调用延迟 0.1 秒(使用 dispatch_after)并解决了这个问题。不过感觉很hacky。我无法轻松测试您的代码,因为 scrollToPoint 不是已定义的函数。
    • 哦,我们在clipView子类中添加了animate:参数,没错。插入后不能立即引用该行似乎很奇怪,即使正在播放动画,但所有这些代码都是相当新的。
    猜你喜欢
    • 2023-03-08
    • 2011-05-18
    • 1970-01-01
    • 2021-12-24
    • 1970-01-01
    • 2018-11-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多