How to scroll to row added to NSTableView after an

2019-02-18 03:27发布

问题:

After adding a new row to an NSTableView I'd like to scroll to it.

When that row has been added to the end of table the scroll only scrolls to the row that was previously the last row. I initially thought that I had to wait for the animation to finish, but that hadn't solved my issue. Here's my code:

        [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];

How can I do this?

回答1:

I found a solution to his problem that I'm happy with:

[_tableView insertRowsAtIndexes:indexSet withAnimation:NSTableViewAnimationEffectGap];

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

I'm simply delaying the scroll request until the next run loop.



回答2:

We had problems with this, so we ended up doing the scroll as the other animations happen, to keep the row on-screen. You’d call this code inside your animation grouping where you do the tableView modifications.

The code looks like this:

- (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;
}