Row Height NSTableView Based on NSStrings

Basically, I have an NSTableView with 1 column, and I insert long rows into each row. However, not all lines are long, so I would like the height of each line to be different depending on the length of the line.

I realized that I need to ask collumn how wide it is, and then ask the line how many lines it will occupy, if collumn is that wide, and then decide how โ€œtallโ€ NSCell will be, But how exactly do I do? I got the column width:

[[[tableView tableColumns] objectAtIndex:0] width];

but I canโ€™t figure out how to ask NSString how much space it will take. Or maybe there is a better way I should do this?

Thanks for any help in advance.

+5
objective-c cocoa nstableview
Jul 09 '10 at 11:52
source share
5 answers

Create an instance of NSTextFieldCell and map its font / size / etc. to the cell cell of the column. Ask him -cellSizeForBounds: passing the rectangle of the required width for a column with a large height (FLT_MAX?). The result should be NSSize, the height of which you can use.

It gets harder if you have more than one column with multiple rows, because you will need to consider all the cells in that row, taking the largest row height. If you expect a lot of lines on average, you probably want to cache this job, updating it as needed, and then just referring to it when you call the delegate method of row height.

+10
Jul 09 '10 at 16:54
source share

Code as per above answer ...

  NSString *string = [[_tableViewDataSourceArray objectAtIndex:rowIndex] valueForKey:@"StringToDisplay"]; NSTableColumn *tableColoumn = [aTableView tableColumnWithIdentifier:@"TableColumnIdentifier"]; if (tableColoumn) { NSCell *dataCell = [tableColoumn dataCell]; [dataCell setWraps:YES]; [dataCell setStringValue:string]; NSRect myRect = NSMakeRect(0, 0, [tableColoumn width], CGFLOAT_MAX); heightOfRow = [dataCell cellSizeForBounds:myRect].height; } 
+8
May 18 '12 at 9:29 a.m.
source share

Here's a slight improvement Robert D'Almeida responds (I originally presented this as editing, but it was rejected because "the original value or intent of the publication will be lost"). I added a method signature and made some other minor changes.

 - (CGFloat)tableView:(NSTableView *)aTableView heightOfRow:(NSInteger)row { CGFloat heightOfRow = 100; // or some other default value // get the content for this table cell from the model NSString *string = [[_tableViewDataSourceArray objectAtIndex:rowIndex] valueForKey:@"StringToDisplay"]; NSTableColumn *tableColumn = [aTableView tableColumnWithIdentifier:@"TableColumnIdentifier"]; if (tableColumn) { NSCell *dataCell = [tableColumn dataCell]; [dataCell setWraps:YES]; [dataCell setStringValue:string]; // See how tall it naturally would want to be if given a restricted // width, but unbound height NSRect myRect = NSMakeRect(0, 0, [tableColumn width], CGFLOAT_MAX); heightOfRow = [dataCell cellSizeForBounds:myRect].height; } return heightOfRow; } 
+3
Jun 17 '14 at 14:26
source share

Here is another solution that works well in my case:

Objective-C:

 - (double)tableView:(NSTableView *)tableView heightOfRow:(long)row { if (tableView == self.tableViewTodo) { CKRecord *record = [self.arrayTodoItemsFiltered objectAtIndex:row]; NSString *text = record[@"title"]; double someWidth = self.tableViewTodo.frame.size.width; NSFont *font = [NSFont fontWithName:@"Palatino-Roman" size:13.0]; NSDictionary *attrsDictionary = [NSDictionary dictionaryWithObject:font forKey:NSFontAttributeName]; NSAttributedString *attrString = [[NSAttributedString alloc] initWithString:text attributes:attrsDictionary]; NSRect frame = NSMakeRect(0, 0, someWidth, MAXFLOAT); NSTextView *tv = [[NSTextView alloc] initWithFrame:frame]; [[tv textStorage] setAttributedString:attrString]; [tv setHorizontallyResizable:NO]; [tv sizeToFit]; double height = tv.frame.size.height + 20; return height; } else { return 18; } } 

Swift:

 func tableView(tableView: NSTableView, heightOfRow row: Int) -> CGFloat { if let log:Log = logsArrayController.arrangedObjects.objectAtIndex(row) as? Log { if let string: String = log.message! { let someWidth: CGFloat = tableView.frame.size.width let stringAttributes = [NSFontAttributeName: NSFont.systemFontOfSize(12)] //change to font/size u are using let attrString: NSAttributedString = NSAttributedString(string: string, attributes: stringAttributes) let frame: NSRect = NSMakeRect(0, 0, someWidth, CGFloat.max) let tv: NSTextView = NSTextView(frame: frame) tv.textStorage?.setAttributedString(attrString) tv.horizontallyResizable = false tv.sizeToFit() let height: CGFloat = tv.frame.size.height + 20 // + other objects... return height } } return 100 //Fail } 
+2
Sep 01 '15 at 13:19
source share

I like Robert D'Almeida's answer , but it uses -[tableColumn dataCell] , which, according to Apple, says "valid only for cellular table views."

My attempt below.

In the Builder interface (in Xcode: I am using 5.1.1 currently), configure NSTableView.

  • the table view must have a content mode set based on the view
  • One of the columns must have a WrapColumnIdentifier
  • this column should contain an NSTableCellView, which should contain an NSTextField (and to understand the code below, you need to know that the NSTextField contains an NSTextFieldCell, which is a subclass of NSCell)
  • NSTextField Object Must Have Wraps Layout Installed
  • An autosave mask should be configured in the NSTextField object, where all six lines are on (solid red), so when the table resizes the table cell, the text box will be resized and will continue to fill the cell.

The following is the NSTableViewDelegate method for use with this setting. This leads to the fact that the height of each row of the table is set so that the column "wrap" (which contains NSTextField) shows its text without truncation.

 - (CGFloat)tableView:(NSTableView *)aTableView heightOfRow:(NSInteger)aRow { NSTableColumn *tableColumn = [aTableView tableColumnWithIdentifier:@"WrapColumnIdentifier"]; // obtain the view that would be used at this position in the table // (ie at this column and row: I'm using this odd form of language so as // to avoid the word "cell" and potential confusion with NSCell), // populated with data from the model NSView* view = [self tableView:aTableView viewForTableColumn:tableColumn row:aRow]; NSAssert([view isKindOfClass:[NSTableCellView class]], @"Expected view used in table to be NSTableCellView"); NSTableCellView* tableCellView = (NSTableCellView*)view; // get the NSCell used by the NSTextField at this position in the table NSCell* cell = [[tableCellView textField] cell]; // See how tall it naturally would want to be if given a restricted width, // but unbound height NSRect unboundHeightColumnRect = NSMakeRect(0, 0, [tableColumn width], CGFLOAT_MAX); CGFloat cellTextHeight = [cell cellSizeForBounds:unboundHeightColumnRect].height; // Apple docs say this method must return a value greater than 0. // cellTextHeight might be 0 (eg if the model returns no data at this // position in the table). Use the row height set for the table in IB // as the minimum. return MAX(cellTextHeight, [tableView rowHeight]); } 
+1
Jun 17 '14 at 15:02
source share



All Articles