Re: NSTableView row heights with auto layout and bindings
Re: NSTableView row heights with auto layout and bindings
- Subject: Re: NSTableView row heights with auto layout and bindings
- From: Jonathan Guy <email@hidden>
- Date: Thu, 02 Jul 2015 00:01:12 +0100
> On 1 Jul 2015, at 09:37, Ken Thomases <email@hidden> wrote:
>
> On Jun 30, 2015, at 11:56 AM, Jonathan Guy <email@hidden> wrote:
>
>> Im trying to fix a problem with dynamic row heights with auto layout and bindings which was working fine in an older build of Xcode but which now no longer works. So my new attempt for the most part works but about 30% of my row heights are not correctly calculated. Basically I create a reference NSTableCellView, set my model object as the objectValue which then updates the bindings, set the frame width of the reference cell to the column width then work out the height from there by setting preferredMaxLayoutWidth on the wrapping text field. Here's the code to do the calculation
>>
>>
>> 1 - (CGFloat)tableView:(NSTableView *)tableView heightOfRow:(NSInteger)row
>> 2 {
>> 3 static NSTableCellView *cellView = nil;
>> 4
>> 5 if (cellView == nil) {
>> 6 NSDictionary *nibs = [tableView registeredNibsByIdentifier];
>> 7 NSNib *nib = nibs[@"MainCellView"];
>> 8 NSArray *topLevelObjects = nil;
>> 9
>> 10 [nib instantiateWithOwner:nil topLevelObjects:&topLevelObjects];
>> 11
>> 12 for (id object in topLevelObjects) {
>> 13 if ([object isKindOfClass:[NSTableCellView class]]) {
>> 14 cellView = object;
>> 15 break;
>> 16 }
>> 17 }
>> 18 }
>> 19
>> 20 LogEntryNode *node = self.logEntryNodes[row];
>> 21
>> 22 cellView.objectValue = node;
>> 23
>> 24 NSTableColumn *tc = tableView.tableColumns[0];
>
> You should always look up table columns by identifier, not order in the array.
I would have done if there where more than one column in the table but as there is only one and there will only ever be one I think it's pretty safe the way it is. I also moved this to a static so I don't have to keep getting it this way.
>
>> 25 NSSize size = NSMakeSize(tc.width, 43.0);
>> 26
>> 27 [cellView setFrameSize:size];
>
> Does the cell view have translatesAutoresizingMaskIntoConstraints turned on? If not, then setting the frame size is useless. You would have to set a width constraint.
No it's not turned on. I don't think this really matters to be honest as the view is not going to be embedded.
>
>> 28 [cellView setNeedsLayout:YES];
>> 29 [cellView layoutSubtreeIfNeeded];
>> 30
>> 31 cellView.textField.preferredMaxLayoutWidth = cellView.textField.frame.size.width;
>> 32
>> 33 CGFloat height = cellView.textField.intrinsicContentSize.height;
>> 34 CGFloat padding = cellView.frame.size.height - cellView.textField.frame.size.height;
>> 35
>> 36 height += padding;
>
> The text field's intrinsic height is in terms of the alignment rect, not the frame rect. You should have the text field convert a rect of the appropriate size from alignment rect to frame rect (using -frameForAlignmentRect:) and get the height of that. Furthermore, what constraints are there within your cell view between the text field and the cell view? What reason to do you have to believe that that padding, computed from when the text field had no (or an incorrect) preferredMaxLayoutWidth, is appropriate for when it has the correct one?
>
> Can you make auto layout determine a height for a container view so that, after setting preferredMaxLayoutWidth, you'd simply do another layout pass and take a container's frame height as the row height?
>
> Regards,
> Ken
>
I disagree with the first part. The auto layout system uses the alignment rect for laying out which comes from the intrinsic content size. The frame rect is just the rect needed to do all the drawing and this is where I realised one of the mistakes I was making. I was setting the preferredMaxLayoutWidth in terms of the frame width of the text field and not the alignment rect width. After correcting this everything now works spot on.
Just to elaborate there were two problems I was having with this. Firstly the first time the view was shown on screen some of the row heights where out by quite a margin. This I discovered was down to me doing the row height calculations before the initial layout pass so when the table came on screen the row heights where all completely wrong.
Secondly after I resized the table width the row heights were a lot better but were still not quite right.
Changing a couple of things fixed these problems
Firstly to notify the table that the row heights needed updating I subclass the table and implemented viewDidEndLiveResize to notify the view controller. I scrapped this and just implemented tableViewColumnDidResize in the view controller which made things easier and fixed the first problem.
Secondly I set the preferredMaxLayoutWidth in terms of the alignment rect width of the text field.
So this all now works nicely and to sum up this is what I did
- (CGFloat)tableView:(NSTableView *)tableView heightOfRow:(NSInteger)row
{
static NSTableCellView *cellView = nil;
static NSTableColumn *tableColumn = nil;
if (cellView == nil) {
// Make the reference cell view
NSDictionary *nibs = [tableView registeredNibsByIdentifier];
NSNib *nib = nibs[@"MainCellView"];
NSArray *topLevelObjects = nil;
[nib instantiateWithOwner:nil topLevelObjects:&topLevelObjects];
for (id object in topLevelObjects) {
if ([object isKindOfClass:[NSTableCellView class]]) {
cellView = object;
break;
}
}
tableColumn = tableView.tableColumns[0];
}
LogEntryNode *node = self.logEntryNodes[row];
cellView.objectValue = node;
NSSize size = NSMakeSize(tableColumn.width, 43.0);
[cellView setFrameSize:size];
[cellView setNeedsLayout:YES];
[cellView layoutSubtreeIfNeeded];
NSRect alignmentRect = [cellView.textField alignmentRectForFrame:cellView.textField.frame];
cellView.textField.preferredMaxLayoutWidth = alignmentRect.size.width;
CGFloat height = cellView.textField.intrinsicContentSize.height + TABLE_CELL_TEXT_FIELD_V_PADDING;
return (height < 43.0) ? 43.0 : height;
}
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden