9

A view-based NSTableView with rows that have dynamic heights doesn't resize its rows when the table view size is changed. This is a problem when the row height is derived from the table view's width (think text blocks that fills a column and wraps thus extending the row size).

I've been trying to get NSTableView to resize its rows whenever it changed size but have experienced little success:

  • If I resize only the visible rows by querying enumerateAvailableRowViewsUsingBlock:, some of the non-visible rows doesn't get resized and thus were shown with the old height when the user scrolls and reveal these rows.
  • If I resize all rows it becomes noticeably slow when there are a lot of rows (about 1 second delay after each window resize for 1000 rows in my 1.8Ghz i7 MacBook Air).

Anybody can help?

This is where I detect the table view size change - in the table view's delegate:

- (void)tableViewColumnDidResize:(NSNotification *)aNotification
{
    NSTableView* aTableView = aNotification.object;
    if (aTableView == self.messagesView) {
        // coalesce all column resize notifications into one -- calls messagesViewDidResize: below

        NSNotification* repostNotification = [NSNotification notificationWithName:BSMessageViewDidResizeNotification object:self];
        [[NSNotificationQueue defaultQueue] enqueueNotification:repostNotification postingStyle:NSPostWhenIdle];
    }
}

Whereas the following is the handler of the notification posted above, where the visible rows get resized:

-(void)messagesViewDidResize:(NSNotification *)notification
{
    NSTableView* messagesView = self.messagesView;

    NSMutableIndexSet* visibleIndexes = [NSMutableIndexSet new];
    [messagesView enumerateAvailableRowViewsUsingBlock:^(NSTableRowView *rowView, NSInteger row) {
        if (row >= 0) {
            [visibleIndexes addIndex:row];
        }
    }];
    [messagesView noteHeightOfRowsWithIndexesChanged:visibleIndexes];   
}

The alternative implementation that resizes all rows looks like this:

-(void)messagesViewDidResize:(NSNotification *)notification
{
    NSTableView* messagesView = self.messagesView;      
    NSIndexSet indexes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0,messagesView.numberOfRows)];      
    [messagesView noteHeightOfRowsWithIndexesChanged:indexes];  
}

Note: This question is somewhat related to View-based NSTableView with rows that have dynamic heights but is more focused towards responding to the table view's size change.

Community
  • 1
  • 1
adib
  • 8,285
  • 6
  • 52
  • 91

1 Answers1

12

I just went through this exact problem. What I did was monitor the NSViewBoundsDidChangeNotification for the scroll view's content view

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(scrollViewContentBoundsDidChange:) name:NSViewBoundsDidChangeNotification object:self.scrollView.contentView];

and in the handler, get the visible rows and call noteHeightOfRowsWithIndexesChange:. I disable animation while doing this so the user doesn't see the rows wiggle during the resize as view's come into the table

- (void)scrollViewContentBoundsDidChange:(NSNotification*)notification
{
    NSRange visibleRows = [self.tableView rowsInRect:self.scrollView.contentView.bounds];
    [NSAnimationContext beginGrouping];
    [[NSAnimationContext currentContext] setDuration:0];
    [self.tableView noteHeightOfRowsWithIndexesChanged:[NSIndexSet indexSetWithIndexesInRange:visibleRows]];
    [NSAnimationContext endGrouping];
}

This has to perform quickly so the table scrolls nicely but its working very well for me.

  • For me I waited till the user have completed resizing then re-adjust the row heights. – adib Sep 06 '12 at 06:39
  • 1
    apparently the scroll view content view doesn't post change notifications, even after calling `[self.scrollView.contentView setPostsBoundsChangedNotifications:YES]` – adib Nov 13 '12 at 04:47
  • 1
    it seems that NSViewFrameDidChangNotification works better for live resizing – Sergey Skopus Aug 29 '14 at 09:33
  • +1 for NSViewFrameDidChangNotification, although it's actually spelled `NSViewFrameDidChangeNotification`, which I quickly found out after Xcode cried at me! – Will Aug 14 '15 at 19:15
  • Thanks a ton bro swift code is follows let column = notification.userInfo?["NSTableColumn"] as? NSTableColumn if let tableView = column?.tableView { print("_____TEST2222222") let visibleRowRange = tableView.rowsInRect(tableView.visibleRect) NSAnimationContext.beginGrouping() NSAnimationContext.currentContext().duration = 0 tableView.noteHeightOfRowsWithIndexesChanged(NSIndexSet(indexesInRange: visibleRowRange)) NSAnimationContext.endGrouping() – Amith Jan 21 '16 at 07:41
  • Thanks for the swift! I'm just starting a new mac app and trying to remember all this errata that I discovered back then and how to do it all in swift! – Robert Olivier Jul 25 '16 at 03:34