After more than a year, I finally figured out a proper answer to my own question.
The trick is to call setNeedsDisplay on every row in the list of visible rows as the NSTableColumn is being resized.
Here's the code I use in my NSViewController subclass:
- (void)tableView:(NSTableView *)tableView isResizingTableColumn:(NSTableColumn *)tableColumn {
NSRange range = [tableView rowsInRect:tableView.visibleRect];
for (NSInteger row = range.location; row < (range.location + range.length); row++) {
NSTableRowView *rowView = [tableView rowViewAtRow:row makeIfNecessary:NO];
[rowView setNeedsDisplay:YES];
}
}
My NSTableView has as its header view my own subclass of NSTableHeaderView. I set my NSViewController as the delegate for the header view so I can track when the user resizes.
TFRecordTableHeaderView.h:
#import <Cocoa/Cocoa.h>
@protocol TFRecordTableHeaderViewDelegate <NSObject>
- (void)tableView:(NSTableView *)tableView isResizingTableColumn:(NSTableColumn *)tableColumn;
@end
@interface TFRecordTableHeaderView : NSTableHeaderView
@property (nonatomic, assign) id<TFRecordTableHeaderViewDelegate>delegate;
@end
TFRecordTableHeaderView.m:
#import "TFRecordTableHeaderView.h"
@implementation TFRecordTableHeaderView
- (id)initWithFrame:(NSRect)frame {
self = [super initWithFrame:frame];
if (self) {
// Initialization code here.
}
return self;
}
- (void)drawRect:(NSRect)dirtyRect {
[super drawRect:dirtyRect];
// do some custom drawing here
}
// track resizing of the column as it happens
// from the docs: If the user is resizing a column in the receiver, returns the index of that column.
- (NSInteger)resizedColumn {
NSInteger columnIndex = [super resizedColumn];
if (columnIndex >= 0) {
NSTableColumn *column = [self.tableView.tableColumns objectAtIndex:columnIndex];
if ([self.delegate respondsToSelector:@selector(tableView:isResizingTableColumn:)]) {
[self.delegate tableView:self.tableView isResizingTableColumn:column];
}
}
return columnIndex;
}
@end
Just set the NSTableHeaderView subclass to your NSTableView's header view. Now whenever you drag a column in the header, the isResizingTableColumn delegate method will be called. You can implement the isResizingTableColumn on your NSViewController subclass.
Now when you resize a column, isResizingTableColumn will be called which will get the NSTableRowViews for the visible rect and it will send setNeedsDisplay. That will cause the rows to be refreshed and the drawBackgroundInRect method to get called while dragging. This in turn will refresh my custom vertical grid lines.
All this to avoid drawing a vertical grid line over top grouped rows on an NSTableView. I think this should be built-in to NSTableView. It looks really bad if your group section headers have text in them and there's a vertical grid line running right over top the text.
Now that I think about it, you could just do this right on your NSTableHeaderView subclass:
- (NSInteger)resizedColumn {
NSInteger columnIndex = [super resizedColumn];
if (columnIndex >= 0) {
NSRange range = [self.tableView rowsInRect:self.tableView.visibleRect];
for (NSInteger row = range.location; row < (range.location + range.length); row++) {
NSTableRowView *rowView = [self.tableView rowViewAtRow:row makeIfNecessary:NO];
[rowView setNeedsDisplay:YES];
}
}
return columnIndex;
}
In my case I was doing it on my NSViewController subclass because while I resized one table column I was also resizing another equally. This was simulating a footer row with another similarly configured table that had only one row in it since NSTableView doesn't have the concept of a footer like UITableView does.