NSTableView行高基于NSStrings

时间:2010-07-09 11:52:13

标签: objective-c cocoa nstableview

基本上,我有一个带有1列的NSTableView,我在每行插入长字符串。但是,并非所有的字符串都很长,所以我希望每行的高度根据字符串的长度而有所不同。

我已经发现我需要询问它的宽度是多少,然后询问字符串如果柱子宽那么会占用多少行,然后决定NSCell的“高”程度。但是我到底该怎么做呢?我从下面得到了柱宽:

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

但我无法弄清楚如何询问NSString将占用多少空间。或者,也许,我应该采取更好的方式来做这件事?

感谢您提前提供任何帮助。

5 个答案:

答案 0 :(得分:10)

创建NSTextFieldCell实例并匹配其字体/大小/等。到列的数据单元格。询问-cellSizeForBounds:,传递一个具有较大高度(FLT_MAX?)的列所需宽度的矩形。结果应该是可以使用其高度的NSSize。

如果您有多个多行文本列会变得更加棘手,因为您需要考虑该行中的所有单元格,将最大值作为行高。如果你期望平均有很多行,你可能想要缓存这项工作,根据需要更新它,然后在调用行高度委托方法时简单地引用它。

答案 1 :(得分:8)

按照上述答案编码...

    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;
    }

答案 2 :(得分:3)

这是Robert D'Almeida's answer的一个小改进(我最初将其作为编辑提交,但它被拒绝了,因为“帖子的原始含义或意图将丢失”)。我添加了方法签名并进行了一些其他小改动。

- (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 :(得分:2)

我喜欢Robert D'Almeida's answer,但它使用的是-[tableColumn dataCell],Apple的博士说#34}仅适用于基于细胞的表格视图"。

我的尝试如下。

在Interface Builder中(在Xcode中:我目前使用5.1.1),设置NSTableView。

  • 表格视图应将内容模式设置为基于查看
  • 其中一列应具有标识符 WrapColumnIdentifier
  • 该列应该包含一个NSTableCellView,它应该包含一个NSTextField(为了理解下面的代码,你需要知道NSTextField包含一个NSTextFieldCell,它是NSCell的一个子类)
  • NSTextField对象应将布局设置为 Wraps
  • NSTextField对象应设置自动调整遮罩,其中所有六行都已启用(红色常亮),因此当表格调整表格单元格大小时,文本字段将调整大小并继续填充单元格。

这是一个与该设置一起使用的NSTableViewDelegate方法。这导致每个表行的高度被设置为使得"包裹列" (包含NSTextField)显示其文本而不截断。

- (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]);
}

答案 4 :(得分:2)

这是另一个解决方案,在我的案例中效果很好:

<强>目标-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;
    }
}

<强>夫特:

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
}