【问题标题】:NSLayoutManager/NSTextContainer Ignores Scale FactorNSLayoutManager/NSTextContainer 忽略比例因子
【发布时间】:2026-01-24 02:20:07
【问题描述】:

我正在尝试使用以下方法在给定宽度的情况下测量NSAttributedString 的高度:

-(CGFloat)calculateHeightForAttributedString:(NSAttributedString*)attributedNotes {
    CGFloat scrollerWidth = [NSScroller scrollerWidthForControlSize:NSRegularControlSize scrollerStyle:NSScrollerStyleLegacy];
    CGFloat width = self.tableView.frame.size.width - self.cellNotesWidthConstraint - scrollerWidth;
    // http://www.cocoabuilder.com/archive/cocoa/54083-height-of-string-with-fixed-width-and-given-font.html
    NSTextView *tv = [[NSTextView alloc] initWithFrame:NSMakeRect(0, 0, width - 20, 1e7)];
    tv.font = [NSFont userFontOfSize:32];
    [tv.textStorage setAttributedString:attributedNotes];
    [self setScaleFactor:[self convertSliderValueToFontScale:self.fontScaleSlider] forTextView:tv];

    [tv.layoutManager glyphRangeForTextContainer:tv.textContainer];
    [tv.layoutManager ensureLayoutForTextContainer:tv.textContainer];

    return [tv.layoutManager usedRectForTextContainer:tv.textContainer].size.height + 10.0f; // add a little bit of a buffer
}

基本上,宽度是表格视图的大小减去滚动条和用于显示其他信息的每个单元格的一点点。只要文本比例(通过convertSliderValueToFontScale:)为1.0,此方法就非常有效。但是,如果我更改比例因子,usedRectForTextContainer 的结果是不正确的 - 好像没有考虑比例因子。

在NSTextView的setScaleFactor:forTextView:中设置比例如下(标量为实际的比例量):

[textView scaleUnitSquareToSize:NSMakeSize(scaler, scaler)];

关于如何解决这个问题的任何想法?

编辑:我有一个示例项目可以在这里尝试:Github。奇怪的是,如果标度

【问题讨论】:

  • 如我所见,scaleUnitSquareToSize 改变了点的大小,局部坐标中的点数保持不变。
  • 我想 NSLayoutManager/NSTextContainer 知道关于scaleUnitSquareToSize某事,因为文本换行会随着文本的放大而调整(假设您的 NSScrollView 禁用了水平滚动)。
  • 没有骰子。我在这里有一个示例项目:github.com/Deadpikle/CocoaStringHeightWithScale。奇怪的是,有一些值有效——但每次运行软件似乎都不同(尽管它总是 ~4)。如果 scale

标签: macos cocoa nstextview nslayoutmanager nstextcontainer


【解决方案1】:

答案很简单:将NSTextView 添加为与NSTextView 具有相同框架的NSClipView 的子视图。

最终的高度函数如下:

-(CGFloat)calculateHeightForAttributedString:(NSAttributedString*)attributedNotes {
    CGFloat width = self.textView.frame.size.width;
    // http://www.cocoabuilder.com/archive/cocoa/54083-height-of-string-with-fixed-width-and-given-font.html
    NSTextView *tv = [[NSTextView alloc] initWithFrame:NSMakeRect(0, 0, width, 1e7)];
    tv.horizontallyResizable = NO;
    tv.font = [NSFont userFontOfSize:32];
    tv.alignment = NSTextAlignmentLeft;
    [tv.textStorage setAttributedString:attributedNotes];
    [self setScaleFactor:self.slider.floatValue forTextView:tv];

    // In order for usedRectForTextContainer: to be accurate with a scale, you MUST
    // set the NSTextView as a subview of an NSClipView!
    NSClipView *clipView = [[NSClipView alloc] initWithFrame:NSMakeRect(0, 0, width, 1e7)];
    [clipView addSubview:tv];

    [tv.layoutManager glyphRangeForTextContainer:tv.textContainer];
    [tv.layoutManager ensureLayoutForTextContainer:tv.textContainer];
    return [tv.layoutManager usedRectForTextContainer:tv.textContainer].size.height;
}

【讨论】: