This is actually my answer to this question on Cocoa-Dev, but I couldn't see how to post a reply there. The existing replies mostly used HTML, and I didn't like that approach. I also couldn't find an answer I liked elsewhere on the internet.
http://lists.apple.com/archives/Cocoa-dev/2007/Jan/msg00801.html
I didn't want to learn HTML to print tabular data. This is the approach I took.
Essentially, I have a separate invisible window with a copy of the displayed NSTableView nested inside a custom NSView called MyPrintableBox. When MyPrintableBox is asked if it knowsPageRange, it calculates the needed pages from the number of rows in the table. When asked for RectForPage it returns the bounds rect but saves the page number for use in the dataSource methods. The dataSource method calls to MyPrintableBox use the dataSource methods of the displayed table to obtain the required data. Some NSTextFields associated with the displayed table needed to be duplicated near the invisible printed table, and these were updated at the time MyPrintableBox was passed to the print method. Some code here was only used while I was testing the approach with the printable window set as being visible. Of course, an NSView could be used instead of an NSBox,
I like this approach because I can initially use the same IB NSTableView and the dataSource methods used for the displayed table. I can later customize the appearance of the printed table to optimize it's printed appearance.
http://lists.apple.com/archives/Cocoa-dev/2007/Jan/msg00801.html
I didn't want to learn HTML to print tabular data. This is the approach I took.
Essentially, I have a separate invisible window with a copy of the displayed NSTableView nested inside a custom NSView called MyPrintableBox. When MyPrintableBox is asked if it knowsPageRange, it calculates the needed pages from the number of rows in the table. When asked for RectForPage it returns the bounds rect but saves the page number for use in the dataSource methods. The dataSource method calls to MyPrintableBox use the dataSource methods of the displayed table to obtain the required data. Some NSTextFields associated with the displayed table needed to be duplicated near the invisible printed table, and these were updated at the time MyPrintableBox was passed to the print method. Some code here was only used while I was testing the approach with the printable window set as being visible. Of course, an NSView could be used instead of an NSBox,
I like this approach because I can initially use the same IB NSTableView and the dataSource methods used for the displayed table. I can later customize the appearance of the printed table to optimize it's printed appearance.
Code:
// Try box size w:600 h:760
@interface MyPrintableBox : NSBox {
int rows, pages, pageNum;
IBOutlet NSTextField *pageDisplay;
IBOutlet NSWindowController *dataSourceWindow;
IBOutlet NSTableView *PrintedTable;
BOOL doingPages;
}
@end
@implementation MyPrintableBox
// The print method in MyDocument requests a printable NSView from MyWindow.
// MyWindow Nib contains an extra invisible window with a copy of the displayed table nested
// within an NSBox of this custom class, MyPrintableBox. MyWindow returns MyPrintableBox
// as the printable NSView requested by MyDocument. MyWindow updates some duplicate
// NSTextFields in myPrintableBox before filling the request.
// The dataSource for the invisible table is MyPrintableBox.
// The dataSource for the displayed table is MyWindow, connected here to dataSourceWindow
int rowsPerPage = 35;
- (id) initWithCoder:(NSCoder *)decoder {
[super initWithCoder:decoder];
rows = 0;
pages = 1;
pageNum = 1;
doingPages = NO; // not needed, used for testing
return self;
}
- (void) calculatePages {
rows = [dataSourceWindow numberOfRowsInTableView:nil]; // nil is ok in my case
pages = (rows / rowsPerPage) + 1;
if (pages > 1)
if ((pages - 1) * rowsPerPage == rows)
pages = pages - 1;
}
- (BOOL) knowsPageRange:(NSRangePointer)range {
[pageDisplay setStringValue:@"Starting Pages"];
[self calculatePages];
range->location = 1;
range->length = pages;
doingPages = YES;
return YES;
}
- (NSRect) rectForPage:(int)page {
pageNum = page;
[pageDisplay setStringValue:[NSString stringWithFormat:@"Page %i of %i", page, pages]];
if (page == pages)
[PrintedTable reloadData]; // otherwise doesn't recall numberOfRowsInTableView
return [self bounds];
}
- (void) endDocument { // not needed, used for testing
[super endDocument];
doingPages = NO;
pageNum = 1;
[PrintedTable reloadData]; // otherwise doesn't recall numberOfRowsInTableView
[pageDisplay setStringValue:@"Finished Doing Pages"];
}
- (int) numberOfRowsInTableView:(NSTableView *)aTableView {
if (!doingPages) {
[self calculatePages];
pageNum = 1;
if (pageNum < pages) {
return rowsPerPage;
} else {
return (rows - ((pageNum - 1) * rowsPerPage));
}
} else if (pageNum < pages) {
return rowsPerPage;
} else
return (rows - ((pageNum - 1) * rowsPerPage));
}
- (id) tableView:(NSTableView *)aTableView
objectValueForTableColumn:(NSTableColumn *)aTableColumn
row:(int)rowIndex {
return [dataSourceWindow tableView:aTableView
objectValueForTableColumn:aTableColumn
row:(((pageNum - 1) * rowsPerPage) + rowIndex)];
}
@end