【问题标题】:Automatically adjust height of a NSTableView自动调整 NSTableView 的高度
【发布时间】:2012-12-22 15:49:25
【问题描述】:

我曾经问过这个问题,但我对解决方案不太满意。

Automatically adjust size of NSTableView

我想在NSPopoverNSWindow 中显示NSTableView
现在,窗口的大小应该会根据表格视图进行调整。

就像 Xcode 一样:



使用自动布局非常简单,您只需将内部视图固定到超级视图即可。

我的问题是,我无法确定表格视图的最佳高度。 下面的代码枚举了所有可用的行,但它没有返回正确的值,因为表视图还有其他元素,如分隔符和表头。

- (CGFloat)heightOfAllRows:(NSTableView *)tableView {
    CGFloat __block height;
    [tableView enumerateAvailableRowViewsUsingBlock:^(NSTableRowView *rowView, NSInteger row) {
        // tried it with this one
        height += rowView.frame.size.height;

        // and this one
        // height += [self tableView:nil heightOfRow:row];
    }];

    return height;
}

1。问题

我该如何解决这个问题?如何正确计算表格视图所需的高度。

2。问题

我应该在哪里运行此代码?
我不想在控制器中实现它,因为它绝对是表格视图应该自己处理的东西。
而且我什至没有找到任何有用的委托方法。

所以我想最好的办法是你可以继承NSTableView
那么我的问题2,在哪里实现呢?


动机

绝对值得赏金

【问题讨论】:

  • 我很确定你的屏幕截图显示了一个菜单...
  • @Richard 我不这么认为,NSMenu 没有滚动条,即使你明白了 ;)
  • @Richard 使用 Accessibility Inspector 进行了查看。绝对是表格视图^^
  • 好吧,很奇怪。这是针对基于视图的表格还是基于单元格的表格?
  • 这是一个基于视图的表格视图

标签: objective-c macos cocoa nstableview autolayout


【解决方案1】:

此答案适用于 Swift 4,针对 macOS 10.10 及更高版本:

1.回答

您可以使用表格视图的fittingSize 来计算弹出框的大小。

tableView.needsLayout = true
tableView.layoutSubtreeIfNeeded()

let height = tableView.fittingSize.height

2。回答

我理解您希望将该代码移出视图控制器的愿望,但由于表视图本身对项目数量(仅通过委托)或模型更改一无所知,因此我会将其放在视图控制器中。从 macOS 10.10 开始,您可以在弹出框内的 NSViewController 上使用 preferredContentSize 来设置大小。

func updatePreferredContentSize() {
    tableView.needsLayout = true
    tableView.layoutSubtreeIfNeeded()

    let height = tableView.fittingSize.height
    let width: CGFloat = 320

    preferredContentSize = CGSize(width: width, height: height)
}

在我的示例中,我使用的是固定宽度,但您也可以使用计算得出的宽度(尚未测试)。

每当您的数据源更改和/或即将显示弹出框时,您都希望调用 update 方法。

希望这能解决你的问题!

【讨论】:

  • 看起来fittingSize 不考虑headerView 的高度,因此您可能需要将其添加到:let totalHeight = outlineView.fittingSize.height + (outlineView.headerView?.frame.height ?? 0)
【解决方案2】:

可以通过查询最后一行的frame来获取table view的高度:

- (CGFloat)heightOfTableView:(NSTableView *)tableView
{
    NSInteger rows = [self numberOfRowsInTableView:tableView];
    if ( rows == 0 ) {
        return 0;
    } else {
        return NSMaxY( [tableView rectOfRow:rows - 1] );
    }
}

这假定一个没有边框的封闭滚动视图!

您可以查询tableView.enclosingScrollView.borderType 来检查滚动视图是否有边框。如果是,则需要将边框宽度添加到结果中(两次;底部和顶部)。不幸的是,我不知道如何获得边框宽度。

查询rectOfRow: 的优势在于它与[tableView reloadData]; 在相同的运行循环迭代中工作。根据我的经验,当您首先执行reloadData 时,查询表格视图的框架并不可靠(您将获得之前的高度)。

【讨论】:

    【解决方案3】:

    我不确定我的解决方案是否比您的解决方案更好,但我想我还是会提供。我将它与打印视图一起使用。我没有使用自动布局。它仅适用于绑定 - 需要调整才能使用数据源。

    你会发现有一个可怕的技巧让它起作用:我只是在我仔细计算的值上加上 0.5。

    这会考虑间距,但不考虑我不显示的标题。如果要显示标题,可以在 -tableView:heightOfRow: 方法中添加。

    NSTableView子类或类别中:

    - (void) sizeHeightToFit {
        CGFloat height = 0.f;
        if ([self.delegate respondsToSelector:@selector(tableView:heightOfRow:)]) {
            for (NSInteger i = 0; i < self.numberOfRows; ++i)
                height = height +
                         [self.delegate tableView:self heightOfRow:i] +
                         self.intercellSpacing.height;
        } else {
            height = (self.rowHeight + self.intercellSpacing.height) *
                     self.numberOfRows;
        }
    
        NSSize frameSize = self.frame.size;
        frameSize.height = height;
        [self setFrameSize:frameSize];
    }
    

    在表视图委托中:

    // Invoke bindings to get the cell contents
    // FIXME If no bindings, use the datasource
    - (NSString *) stringValueForRow:(NSInteger) row column:(NSTableColumn *) column {
        NSDictionary *bindingInfo = [column infoForBinding:NSValueBinding];
    
        id object = [bindingInfo objectForKey:NSObservedObjectKey];
        NSString *keyPath = [bindingInfo objectForKey:NSObservedKeyPathKey];
    
        id value = [[object valueForKeyPath:keyPath] objectAtIndex:row];
    
        if ([value isKindOfClass:[NSString class]])
            return value;
        else
            return @"";
    }
    
    - (CGFloat) tableView:(NSTableView *) tableView heightOfRow:(NSInteger) row {
    
        CGFloat result = tableView.rowHeight;
    
        for (NSTableColumn *column in tableView.tableColumns) {
            NSTextFieldCell *dataCell = column.dataCell;
            if (![dataCell isKindOfClass:[NSTextFieldCell class]]) continue;
    
            // Borrow the prototype cell, and set its text
            [dataCell setObjectValue:[self stringValueForRow:row column:column]];
    
            // Ask it the bounds for a rectangle as wide as the column
            NSRect cellBounds = NSZeroRect;
            cellBounds.size.width = [column width]; cellBounds.size.height = FLT_MAX;
            NSSize cellSize = [dataCell cellSizeForBounds:cellBounds];
    
            // This is a HACK to make this work.
            // Otherwise the rows are inexplicably too short.
            cellSize.height = cellSize.height + 0.5;
    
            if (cellSize.height > result)
                result = cellSize.height;
        }
    
        return result;
    }
    

    【讨论】:

    • 嘿伙计,非常感谢。对此,我真的非常感激。但是,我选择了 Geoffrey Marshall 的答案,因为他没有任何肮脏的技巧就可以正常工作。
    【解决方案4】:

    只要得到-[NSTableView frame] NSTableView 嵌入在 NSScrollView 中,但具有完整大小。

    【讨论】:

    • 我不是在寻找框架,我在寻找最佳框架
    • 如果你有 100 行,frame.height 将是你所需要的。
    • 我需要知道 2、3 或 4 行的高度。空白也属于表格视图。
    【解决方案5】:

    Xcode 中的 Interface Builder 会自动将 NSTableView 放入 NSScrollView。 NSScrollView 是标题实际所在的位置。在窗口中创建一个 NSScrollView 作为您的基本视图并将 NSTableView 添加到其中:

    NSScrollView * scrollView = [[NSScrollView alloc]init];
    [scrollView setHasVerticalScroller:YES];
    [scrollView setHasHorizontalScroller:YES];
    [scrollView setAutohidesScrollers:YES];
    [scrollView setBorderType:NSBezelBorder];
    [scrollView setTranslatesAutoresizingMaskIntoConstraints:NO];
    
    NSTableView * table = [[NSTableView alloc] init];
    [table setDataSource:self];
    [table setColumnAutoresizingStyle:NSTableViewUniformColumnAutoresizingStyle];
    [scrollView setDocumentView:table];
    
    //SetWindow's main view to scrollView
    

    现在你可以查询scrollView的contentView来找到NSScrollView的大小

    NSRect rectOfFullTable = [[scrollView contentView] documentRect];
    

    因为 NSTableView 位于 NSScrollView 内部,所以 NSTableView 将有一个 headerView,您可以使用它来查找标题的大小。

    您可以通过覆盖 reflectScrolledClipView: 方法子类化 NSScrollView 以在表大小更改(标题 + 行)时更新它的超级视图

    【讨论】:

    • 感谢您的回答。但它实际上是关于行,而不是列。我尝试了您的解决方案,只是返回带有所有不必要空格的表格视图的高度。
    • 是的。 contentView 是一个 NSClipView,它永远不会小于你的 NSScrollView 的大小。 NSTableView 依赖于在 NSScrollView 内绘制标题并最小化呈现的控件以节省内存。所以你的 NSScrollView 必须有一个最小尺寸(即使它非常非常小)。然后,您可以将滚动视图的父视图或窗口绑定为 NSScrollView 的完整所需大小。
    • 我不确定这对我有什么帮助...问题是,NSTableView 有时小于滚动视图或剪辑视图,这就是重点...
    • 如果您的 NSScrollView 的最小尺寸非常非常小(1px x 1px),则 NSTableView 不会小于您的 NSScrollView。
    • 但它不仅是标题,它也是分隔符,我什至没有在我正在测试的表格视图中使用标题。如果你愿意,我可以将项目上传到 Github,你会发现它没有返回正确的大小......
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-10-17
    • 2012-11-06
    • 2014-01-14
    • 2011-08-23
    • 2012-08-21
    • 2013-11-12
    • 1970-01-01
    相关资源
    最近更新 更多