简体   繁体   中英

View-Based NSTableView

I have been beating my head against the wall trying to come up with an understanding of tableView:heightOfRow: and I guess I need help. I want to do dynamic row heights for a textView inside the row, and cannot seem to get a handle on the approach. I've read everything I can find, which really isn't much. I can get the rows to size like I want using this method, but only if the table is resized. Rows that are not in view won't be sized right until they are visible and the table is resized.

I've added the tableView:didAddRowView:forRow method and using the same basic idea it ends up squishing the row size to a single line. Doesn't work the same as tableView:heightOfRow: at all, even though it's the same code. I'm guessing that the tableView:didAddRowView:forRow method setting the textView bounds is somehow getting scaled.

Here's my (hopefully relevant) code:

- (CGFloat)tableView:(NSTableView *)tv heightOfRow:(NSInteger)row {
    if (tv == dataTableView) {
        NSInteger valueCol = [tv columnWithIdentifier:@"value"];
        NSTableCellView *valueView = [tv viewAtColumn:valueCol row:row makeIfNecessary:NO];

        if (valueView) {
            // Working on the interesting column and this row is visible
            NSRect bounds = [[valueView textField] bounds];
            id value = [[valueView textField] stringValue];

            NSFont *fieldFont = [[valueView textField] font];
            CGFloat adjustedHeight = [value heightForWidth:bounds.size.width font:fieldFont];
            CGFloat rowHeight = [tv rowHeight];
            if (adjustedHeight <= rowHeight) adjustedHeight = rowHeight;

            return adjustedHeight;
        }
    }
    return [tv rowHeight];
}


- (void)tableView:(NSTableView *)tv didAddRowView:(NSTableRowView *)rowView forRow:(NSInteger)row {
    if (tv == dataTableView) {
        NSInteger valueCol = [tv columnWithIdentifier:@"value"];
        NSTableCellView *colView = [rowView viewAtColumn:valueCol];
        NSRect textFieldViewBounds = [[colView textField] bounds];
        NSTextField *colTextField = [colView textField];
        NSFont *colFont = [colTextField font];
        id value = [colTextField stringValue];
        CGFloat newHeight = [value heightForWidth:textFieldViewBounds.size.width font:colFont];
        NSSize colViewSize = colView.bounds.size;
        colViewSize.height = newHeight;
        textFieldViewBounds.size.height = newHeight;
        [colTextField setBounds:textFieldViewBounds];
    }
}

UPDATE: new code is working better, but still has glitches on initial load and sometimes on scroll:

- (CGFloat)tableView:(NSTableView *)tv heightOfRow:(NSInteger)row {
    if (tv == dataTableView) {
        NSInteger valueCol = [tv columnWithIdentifier:@"value"];
        NSTableCellView *valueView = [tv viewAtColumn:valueCol row:row makeIfNecessary:NO];

        if (valueView) {
            // Working on the interesting column
            NSRect bounds = [[valueView textField] bounds];
            id value = [[valueView textField] stringValue];

            NSFont *fieldFont = [[valueView textField] font];
            CGFloat adjustedHeight = [value heightForWidth:bounds.size.width font:fieldFont];
            CGFloat rowHeight = [tv rowHeight];
            if (adjustedHeight <= rowHeight) adjustedHeight = rowHeight;

            return adjustedHeight;
        }
    }
    return [tv rowHeight];
}


- (void) tableView:(NSTableView *)tv didAddRowView:(NSTableRowView *)rowView forRow:(NSInteger)row {
    if (tv == dataTableView) {
        [dataTableView noteHeightOfRowsWithIndexesChanged:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(row, 1)]];
    }
}

From your code, I'm guessing you want row height to change based on a the number of lines in a textfield cell, right? So the height should change if you resize a column or the table, or if you enter new text...?

IMO you don't need to do anything in -tableView:didAddRowViewForRow: It's too late for that and the row view should already have it's final height. Trying to modify view frame there may cause exceptions of bugs. Since your text field is in a NSTableCellView (assuming appropriate layout constraints, the default ones should be ok), it should display as intended given your design in IB and the row height returned by -tableView:heigthOfRow:

-tableView:heigthOfRow: is indeed the method where you need to determine row height. But you need to return an appropriate value for all rows for which to method is called or else rows won't display at the right height when they enter the visible rect during scrolling. So I would choose YES for the makeIfNecessary: argument of the viewAtColumn method.

Then you need to have the table update the row height when necessary (resizing a column, the view, maybe entering new text...). This could be done for instance in -tableViewColumnDidResize (using the userinfo dictionary to get the resized column) or when the text field is edited. There, you need to call -noteHeightOfRowsWithIndexesChanged: on the table view. If the column is resized, you call it for all rows and the -tableView:heigthOfRow: will return all the heights. If the text field is edited, you may want to call it only for its row (but even calling the method for all rows should do it).

I hope it helped.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM