Basically, I have an NSTableView with 1 collumn, and I'm inserting long strings into each row. However, not all the strings are long, so I'd like the height of each row to be different based on how long the string is.
I've figured out that I need to ask the collumn how wide it is, and then ask the string how many lines it will take up if the collumn is that wide, and then decide 开发者_运维技巧how "tall" the NSCell will be. But how exactly do I go about doing that? I've gotten the collumn width from:
[[[tableView tableColumns] objectAtIndex:0] width];
but I can't figure out how to ask the NSString how much space it will take up. Or, perhaps, there is a better way that I should go about doing this?
Thanks for any help in advance.
Create an NSTextFieldCell instance and match its font/size/etc. to that of your column's data cell. Ask it for the -cellSizeForBounds:
, passing in a rect of the desired width for the column, with a large height (FLT_MAX?). The result should be an NSSize whose height you can use.
It gets trickier if you have more than one multi-line text column because you'll need to consider all the cells in that row, taking the largest as your row height. If you expect a lot of rows on average, you'll probably want to cache this work, updating it as necessary, then simply referencing it when the row height delegate method is called.
Code as per the 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;
}
Here's a small improvement on Robert D'Almeida's answer (I originally submitted this as an edit, but it was rejected because "the original meaning or intent of the post would be lost"). I've added the method signature and made a few other small 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;
}
Here is another solution, which 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
}
I like Robert D'Almeida's answer, but it uses -[tableColumn dataCell]
, which Apple's docs say "is only valid for cell-based table views".
My attempt is below.
In Interface Builder (in Xcode: I'm using 5.1.1 currently), set up an NSTableView.
- the table view should have Content Mode set to View Based
- one of the columns should have the identifier WrapColumnIdentifier
- that 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)
- the NSTextField object should have Layout set to Wraps
- the NSTextField object should have an autosizing mask set up where all six lines are enabled (solid red), so when the table resizes the table cell, the text field will be resized and continue to fill the cell.
Here is a NSTableViewDelegate method for use with that setup. This causes the height of each table row to be set such that the "wrap column" (which contains the 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's 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]);
}
精彩评论