1
votes

I've a NSTableView inside an NSScrollView. The NSTableView's content is provided via bindings to an NSArrayController that is bound to a CoreData ManagedObjectContext. New lines to the NSTableView are just added at and never removed or reordered.

I'd like to keep the NSTableView (or respectively the NSScrollView) pinned down to the bottom, so that new lines that were added to the table become visible immediately. Additionally, this shall just happen, when the NSTableView is scrolled to the bottom and not when the user scrolled upwards to review other rows.

I'm currently using the following code

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

This works so far, but just when the window is active. When the window is inactive, the didAddRowView method doesn't get called until the window becomes active again. Thus the NSTableView does not scroll down immediately. As the NSTableView also gets filled via CoreData bindings I don't know any point where I could just manually trigger the scrolling once a new item has been inserted. Additionally, the didAddRowView delegate method gets called quite often (e.g. when scrolling the table manually) and there is only really the need to scroll down once a new item has been inserted making the solution even less elegant.

Is there any point where I could hook into the insertion process or the NSScrollView directly to scroll down to the bottom when the application is active and inactive just when a new row is added to the NSTableView?

Edit: For the sake of completion or in case the solution of @rdelmar does not work for you, I've subclassed the NSTableView in question and implemented this in the reloadData method

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

The reloadData method gets called once the new item is received in the ArrayController and shall be added to the table. After calling reloadData of the superclass, this checks if the TableView is scrolled to the bottom (the last row of the TableView is the last row that is visible in the superview - hence the surrounding NSScrollView). If so, it scrolls down. If not, the user likely scrolled up to review some content of the table and does not want it to scroll down automatically.

1
How are you adding new data to your core data database? Is it under user action, or is it updating automatically, through server downloads or something like that. – rdelmar

1 Answers

0
votes

I think you should be able to use the NSManagedObjectContext notification, NSManagedObjectContextObjectsDidChangeNotification. Create a custom NSTableView class and add an observer for that notification:

@implementation CustomTable

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