返回错误大小的 NSAttributedString

我正在尝试获取属性化字符串的 rect,但是 bound ingRectWithSize 调用不尊重我传入的大小,并且返回一个单行高度的 rect,而不是一个大高度的 rect (它是一个长字符串)。我曾经尝试过像下面的代码那样传入一个非常大的 height 值和0,但是返回的 rect 值总是相同的。

CGRect paragraphRect = [attributedText boundingRectWithSize:CGSizeMake(300,0.0)
options:NSStringDrawingUsesDeviceMetrics
context:nil];

这个是坏了,还是我需要做一些其他的事情来让它返回一个包装文本的 rect?

133689 次浏览

看起来你没有提供正确的选项。对于包装标签,至少提供:

CGRect paragraphRect =
[attributedText boundingRectWithSize:CGSizeMake(300.f, CGFLOAT_MAX)
options:(NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading)
context:nil];

注意: 如果原始文本宽度小于300。如果没有行包装,那么请确保绑定的大小是正确的,否则仍然会得到错误的结果。

- Ed McManus 肯定提供了一把让这起案子起作用的钥匙-我发现了一个不起作用的案子

UIFont *font = ...
UIColor *color = ...
NSDictionary *attributesDictionary = [NSDictionary dictionaryWithObjectsAndKeys:
font, NSFontAttributeName,
color, NSForegroundColorAttributeName,
nil];


NSMutableAttributedString *string = [[NSMutableAttributedString alloc] initWithString: someString attributes:attributesDictionary];


[string appendAttributedString: [[NSAttributedString alloc] initWithString: anotherString];


CGRect rect = [string boundingRectWithSize:constraint options:(NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading) context:nil];

Rect 将不具有正确的高度。请注意,初始化 另一个字符串(附加在 绳子之后)时没有使用属性字典。这是 另一个字符串的合法初始化器,但是在这种情况下 返回文章页面大小:没有给出准确的大小。

我也遇到过同样的问题,不能使用这些技术获得准确的尺寸,我已经改变了我的方法,使其工作。

我有一个长的属性字符串,我一直试图适应一个滚动视图,以便它显示正确而不被截断。为了使文本可靠地工作,我所做的就是根本不设置高度作为约束,而是允许内部大小取而代之。现在文本显示正确,没有被截断,我不必计算高度。

我想如果我确实需要可靠地得到高度,我会创建一个视图,这是隐藏的和这些约束,并得到框架的高度一旦约束应用。

    NSAttributedString *attributedText =[[[NSAttributedString alloc]
initWithString:joyMeComment.content
attributes:@{ NSFontAttributeName: [UIFont systemFontOfSize:TextFont]}] autorelease];


CGRect paragraphRect =
[attributedText boundingRectWithSize:CGSizeMake(kWith, CGFLOAT_MAX)
options:(NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading)
context:nil];
contentSize = paragraphRect.size;


contentSize.size.height+=10;
label.frame=contentSize;

如果标签的框架不加10这个方法将永远不会工作! 希望这能帮助你! 祝你好运。

我也遇到了同样的问题,但我意识到身高限制设置是正确的,所以我做了以下事情:

-(CGSize)MaxHeighForTextInRow:(NSString *)RowText width:(float)UITextviewWidth {


CGSize constrainedSize = CGSizeMake(UITextviewWidth, CGFLOAT_MAX);


NSDictionary *attributesDictionary = [NSDictionary dictionaryWithObjectsAndKeys:
[UIFont fontWithName:@"HelveticaNeue" size:11.0], NSFontAttributeName,
nil];


NSMutableAttributedString *string = [[NSMutableAttributedString alloc] initWithString:RowText attributes:attributesDictionary];


CGRect requiredHeight = [string boundingRectWithSize:constrainedSize options:NSStringDrawingUsesLineFragmentOrigin context:nil];


if (requiredHeight.size.width > UITextviewWidth) {
requiredHeight = CGRectMake(0, 0, UITextviewWidth, requiredHeight.size.height);
}


return requiredHeight.size;
}

我想加入我的想法,因为我有完全相同的问题。

我之所以使用 UITextView,是因为它有更好的文本对齐方式(当时在 UILabel中还没有这种方式) ,但是为了“模拟”非交互式的、不可滚动的 UILabel,我会完全关闭滚动、弹跳和用户交互。

当然,问题在于文本是动态的,虽然宽度是固定的,但是每次设置新的文本值时,高度都应该重新计算。

boundingRectWithSize对我来说根本不起作用,在我看来,UITextView在上面添加了一些边缘,而 boundingRectWithSize不会进入计数,因此,从 boundingRectWithSize检索到的高度比它应该的要小。

由于文本不能快速更新,它只是用于一些可能每2-3秒更新一次的信息,我决定采用以下方法:

/* This f is nested in a custom UIView-inherited class that is built using xib file */
-(void) setTextAndAutoSize:(NSString*)text inTextView:(UITextView*)tv
{
CGFloat msgWidth = tv.frame.size.width; // get target's width


// Make "test" UITextView to calculate correct size
UITextView *temp = [[UITextView alloc] initWithFrame:CGRectMake(0, 0, msgWidth, 300)]; // we set some height, really doesn't matter, just put some value like this one.
// Set all font and text related parameters to be exact as the ones in targeted text view
[temp setFont:tv.font];
[temp setTextAlignment:tv.textAlignment];
[temp setTextColor:tv.textColor];
[temp setText:text];


// Ask for size that fits :P
CGSize tv_size = [temp sizeThatFits:CGSizeMake(msgWidth, 300)];


// kill this "test" UITextView, it's purpose is over
[temp release];
temp = nil;


// apply calculated size. if calcualted width differs, I choose to ignore it anyway and use only height because I want to have width absolutely fixed to designed value
tv.frame = CGRectMake(tv.frame.origin.x, tv.frame.origin.y, msgWidth, tv_size.height );
}

* 上面的代码不是直接从我的源代码复制过来的,我不得不从本文不需要的一堆东西中调整/清除它。不要把它当成复制粘贴就可以工作的代码。

明显的缺点是,对于每个调用,它都有 alloc 和 release。

但是,这样做的好处是,您可以避免依赖于 bound ingRectWithSize 绘制文本的方式与计算它的大小以及 UITextView中文本绘制的实现之间的兼容性(或者 UILabel,您也可以用 UILabel替换 UITextView)。苹果可能存在的任何“漏洞”都可以通过这种方式避免。

附言。似乎你不应该需要这个“临时”UITextView,可以直接从目标问 sizeThatFits,但是这不适合我。尽管从逻辑上讲,它应该可以工作,而且不需要分配/释放临时 UITextView,但它并没有这样做。但是这个解决方案在我设置的任何文本中都能完美地工作。

@ warrenm 不好意思,框架设置法对我不起作用。

我能搞定。这个函数可以帮助我们确定给定宽度的 iphone/Ipad SDK 中 NSAttributedString 字符串范围所需的帧大小:

它可以用于 UITableView 单元格的动态高度

- (CGSize)frameSizeForAttributedString:(NSAttributedString *)attributedString
{
CTTypesetterRef typesetter = CTTypesetterCreateWithAttributedString((CFAttributedStringRef)attributedString);
CGFloat width = YOUR_FIXED_WIDTH;


CFIndex offset = 0, length;
CGFloat y = 0;
do {
length = CTTypesetterSuggestLineBreak(typesetter, offset, width);
CTLineRef line = CTTypesetterCreateLine(typesetter, CFRangeMake(offset, length));


CGFloat ascent, descent, leading;
CTLineGetTypographicBounds(line, &ascent, &descent, &leading);


CFRelease(line);


offset += length;
y += ascent + descent + leading;
} while (offset < [attributedString length]);


CFRelease(typesetter);


return CGSizeMake(width, ceil(y));
}

感谢 HADDAD ISSA > > http://haddadissa.blogspot.in/2010/09/compute-needed-heigh-for-fixed-width-of.html

textView.textContainerInset = UIEdgeInsetsZero;
NSString *string = @"Some string";
NSDictionary *attributes = @{NSFontAttributeName:[UIFont systemFontOfSize:12.0f], NSForegroundColorAttributeName:[UIColor blackColor]};
NSAttributedString *attributedString = [[NSAttributedString alloc] initWithString:string attributes:attributes];
[textView setAttributedText:attributedString];
CGRect textViewFrame = [textView.attributedText boundingRectWithSize:CGSizeMake(CGRectGetWidth(self.view.frame)-8.0f, 9999.0f) options:(NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading) context:nil];
NSLog(@"%f", ceilf(textViewFrame.size.height));

所有字体都能用!

    NSDictionary *stringAttributes = [NSDictionary dictionaryWithObjectsAndKeys:
[UIFont systemFontOfSize:18], NSFontAttributeName,
[UIColor blackColor], NSForegroundColorAttributeName,
nil];


NSAttributedString *attributedString = [[NSAttributedString alloc] initWithString:myLabel.text attributes:stringAttributes];
myLabel.attributedText = attributedString; //this is the key!


CGSize maximumLabelSize = CGSizeMake (screenRect.size.width - 40, CGFLOAT_MAX);


CGRect newRect = [myLabel.text boundingRectWithSize:maximumLabelSize
options:(NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading)
attributes:stringAttributes context:nil];


self.myLabelHeightConstraint.constant = ceilf(newRect.size.height);

我尝试了这个页面上的所有内容,但仍然有一个 UILabel 格式不正确的例子。实际上,在标签上设置 AttributedText 最终解决了这个问题。

如果您希望通过截断尾部获得边界框,这个问题可以帮助您解决这个问题。

CGFloat maxTitleWidth = 200;


NSMutableParagraphStyle *paragraph = [[NSMutableParagraphStyle alloc] init];
paragraph.lineBreakMode = NSLineBreakByTruncatingTail;


NSDictionary *attributes = @{NSFontAttributeName : self.textLabel.font,
NSParagraphStyleAttributeName: paragraph};


CGRect box = [self.textLabel.text
boundingRectWithSize:CGSizeMake(maxTitleWidth, CGFLOAT_MAX)
options:(NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading)
attributes:attributes context:nil];

经过长时间的调查,我的最终决定是:
函数返回正确的大小不间断的字符序列只! 如果字符串包含空格或其他东西(苹果称之为“一些象形文字”)-这是不可能得到实际大小的直接需要显示文本!
我用字母替换了字符串中的空格,立即得到了正确的结果

苹果表示: Https://developer.apple.com/documentation/foundation/nsstring/1524729-boundingrectwithsize

”此方法返回字符串中字形的实际边界。一些字形(例如空格)被允许与传入的大小指定的布局约束重叠,因此在某些情况下,返回的 CGRect的大小组件的宽度值可以超过 size 参数的宽度值。”

因此,有必要找到另一种方法来计算实际的直接误差。


经过长时间的调查过程终于找到了解决方案! ! ! 我不确定它是否适用于所有与 UITextView相关的病例,但是主要和重要的事情被检测到了!

当使用正确的矩形时,boundingRectWithSize函数和 CTFramesetterSuggestFrameSizeWithConstraints(以及许多其他方法)将正确计算大小和文本部分。 例如-UITextViewtextView.bounds.size.width-这个值不是系统在 UITextView上绘制文本时实际使用的矩形。

我发现了非常有趣的参数,并用代码进行了简单的计算:

CGFloat padding = textView.textContainer.lineFragmentPadding;
CGFloat  actualPageWidth = textView.bounds.size.width - padding * 2;

还有魔法——我所有的短信都计算正确了! 好好享受吧!

由于某些原因,bound ingRectWithSize 总是返回错误的大小。 我想到了一个办法。 有一个用于 UItextView-sizeThatFits 的方法,该方法返回文本集的适当大小。 因此,不要使用 bound ingRectWithSize,而是创建一个带有随机框架的 UITextView,并使用各自的宽度和 CGFLOAT _ MAX 高度调用它的 sizeThatFits。 它返回具有适当高度的大小。

   UITextView *view=[[UITextView alloc] initWithFrame:CGRectMake(0, 0, width, 10)];
view.text=text;
CGSize size=[view sizeThatFits:CGSizeMake(width, CGFLOAT_MAX)];
height=size.height;

如果你在 while 循环中计算大小,不要忘记在自动发布池中添加它,因为会创建 n 个 UITextView,如果我们不使用自动发布池,应用程序的运行时内存将会增加。

我注意到的一件事是,从 (CGRect)boundingRectWithSize:(CGSize)size options:(NSStringDrawingOptions)options attributes:(NSDictionary *)attributes context:(NSStringDrawingContext *)context返回的直角会比我传入的宽度更大。当这种情况发生时,我的字符串将被截断。我是这样解决的:

NSString *aLongString = ...
NSInteger width = //some width;
UIFont *font = //your font;
CGRect rect = [aLongString boundingRectWithSize:CGSizeMake(width, CGFLOAT_MAX)
options:(NSStringDrawingUsesFontLeading | NSStringDrawingUsesLineFragmentOrigin)
attributes:@{ NSFontAttributeName : font,
NSForegroundColorAttributeName : [UIColor whiteColor]}
context:nil];


if(rect.size.width > width)
{
return rect.size.height + font.lineHeight;
}
return rect.size.height;

对于一些更多的上下文,我有多行文本,我试图找到正确的高度来显示它。Bound RectWithSize 有时返回的宽度比我指定的要大,因此当我使用过去的宽度和计算的高度来显示文本时,它会被截断。通过测试 bound ingRectWithSize 使用了错误的宽度,它使高度变短的数量是1行。所以我会检查宽度是否更大,如果是这样,添加字体的 line Height 以提供足够的空间来避免截断。

我对这些建议一点运气都没有。我的字符串包含 Unicode 项目符号点,我怀疑它们在计算中造成了麻烦。我注意到 UITextView 对绘图处理得很好,所以我希望利用它的计算。我执行了以下操作,这可能不如 NSString 绘图方法那样优化,但至少它是准确的。这也比初始化一个 UITextView 仅仅调用 -sizeThatFits:要稍微优化一些。

NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:CGSizeMake(width, CGFLOAT_MAX)];
NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init];
[layoutManager addTextContainer:textContainer];


NSTextStorage *textStorage = [[NSTextStorage alloc] initWithAttributedString:formattedString];
[textStorage addLayoutManager:layoutManager];


const CGFloat formattedStringHeight = ceilf([layoutManager usedRectForTextContainer:textContainer].size.height);

我发现首选的解决方案不处理换行。

我发现这种方法在所有情况下都适用:

UILabel* dummyLabel = [UILabel new];
[dummyLabel setFrame:CGRectMake(0, 0, desiredWidth, CGFLOAT_MAX)];
dummyLabel.numberOfLines = 0;
[dummyLabel setLineBreakMode:NSLineBreakByWordWrapping];
dummyLabel.attributedText = myString;
[dummyLabel sizeToFit];
CGSize requiredSize = dummyLabel.frame.size;
Add Following methods in ur code for getting correct size of attribute string
1.
- (CGFloat)findHeightForText:(NSAttributedString *)text havingWidth:(CGFloat)widthValue andFont:(UIFont *)font
{
UITextView *textView = [[UITextView alloc] init];
[textView setAttributedText:text];
[textView setFont:font];
CGSize size = [textView sizeThatFits:CGSizeMake(widthValue, FLT_MAX)];
return size.height;

}

2. Call on heightForRowAtIndexPath method
int h = [self findHeightForText:attrString havingWidth:yourScreenWidth andFont:urFont];

进出口有点晚的游戏-但我一直在努力找出一种方法,找到一个边界框,将适合周围的属性字符串,使焦点环像编辑一个文件在 Finder 做。当字符串末尾有空格或字符串内有多个空格时,我尝试的所有方法都失败了。boundingRectWithSizeCTFramesetterCreateWithAttributedString都因此惨遭失败。

使用 NSLayoutManager,下面的代码似乎在我目前为止发现的所有情况下都起到了作用,并返回一个完美地限定了字符串边界的 rect。额外的好处: 如果你选择了文本,那么选区的边缘就会直接到达返回的矩形的边界。下面的代码使用来自 NSTextView的 layoutManager。

NSLayoutManager* layout = [self layoutManager];
NSTextContainer* container = [self textContainer];


CGRect focusRingFrame = [layout boundingRectForGlyphRange:NSMakeRange(0, [[self textStorage] length]) inTextContainer:container];

原来,NSAttributedString 的每个部分都必须有一个字典集,其中至少包含 NSFontAttributeName 和 NSForeoundColorAttributeName 集,如果您希望 bound ingRectWithSize 能够实际工作的话!

我没看到任何记录。

斯威夫特四版

let string = "A great test string."
let font = UIFont.systemFont(ofSize: 14)
let attributes: [NSAttributedStringKey: Any] = [.font: font]
let attributedString = NSAttributedString(string: string, attributes: attributes)
let largestSize = CGSize(width: bounds.width, height: .greatestFiniteMagnitude)


//Option one (best option)
let framesetter = CTFramesetterCreateWithAttributedString(attributedString)
let textSize = CTFramesetterSuggestFrameSizeWithConstraints(framesetter, CFRange(), nil, largestSize, nil)


//Option two
let textSize = (string as NSString).boundingRect(with: largestSize, options: [.usesLineFragmentOrigin , .usesFontLeading], attributes: attributes, context: nil).size


//Option three
let textSize = attributedString.boundingRect(with: largestSize, options: [.usesLineFragmentOrigin , .usesFontLeading], context: nil).size

使用 CTFramesetter 测量文本效果最好,因为它提供了整数大小,可以很好地处理表情符号和其他 Unicode 字符。

我花了很多时间调试这个。我发现由 boundingRectWithSize定义的最大文本高度允许我的 UITextView显示文本低于帧大小。

在我的情况下,帧是最多140点,但 UITextView 容忍文本最多131点。

我必须手动计算出来,并硬编码“真正的”最大高度。

我的解决办法是:

- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
NSString *proposedText = [textView.text stringByReplacingCharactersInRange:range withString:text];
NSMutableAttributedString *attributedText = [[NSMutableAttributedString alloc] initWithString:proposedText];
CGRect boundingRect;
CGFloat maxFontSize = 100;
CGFloat minFontSize = 30;
CGFloat fontSize = maxFontSize + 1;
BOOL fit;
NSLog(@"Trying text: \"%@\"", proposedText);
do {
fontSize -= 1;
//XXX Seems like trailing whitespaces count for 0. find a workaround
[attributedText addAttribute:NSFontAttributeName value:[textView.font fontWithSize:fontSize] range:NSMakeRange(0, attributedText.length)];
CGFloat padding = textView.textContainer.lineFragmentPadding;
CGSize boundingSize = CGSizeMake(textView.frame.size.width - padding * 2, CGFLOAT_MAX);
boundingRect = [attributedText boundingRectWithSize:boundingSize options:NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading context:nil];
NSLog(@"bounding rect for font %f is %@; (max is %f %f). Padding: %f", fontSize, NSStringFromCGRect(boundingRect), textView.frame.size.width, 148.0, padding);
fit =  boundingRect.size.height <= 131;
} while (!fit && fontSize > minFontSize);
if (fit) {
self.textView.font = [self.textView.font fontWithSize:fontSize];
NSLog(@"Fit!");
} else {
NSLog(@"No fit");
}
return fit;
}

遇到了完全相同的问题。

对我来说,这个问题是由 属性标签解决的

+ (CGSize)sizeThatFitsAttributedString:(NSAttributedString *)attributedString
withConstraints:(CGSize)size
limitedToNumberOfLines:(NSUInteger)numberOfLines

方法,因为它提供了准确的结果。

2022年7月最新情况

经过更多的尝试和错误,并从其他答案中得到反馈,特别是那些指出要使用 UsesDeviceMetrics的答案,我发现这个选项绝对是一个游戏规则的改变者,尽管它本身还不够。

使用 .deviceMetrics返回正确的 height,但是在某些情况下,它不能适合 UILabelNSTextField

我唯一能让它适合 所有案件的方法就是使用 CATextLayer。IOS 和 macOS 都可以使用。

例子

let attributedString = NSAttributedString(string: "my string")
let maxWidth = CGFloat(300)
let size = attributedString.boundingRect(
with: .init(width: maxWidth,
height: .greatestFiniteMagnitude),
options: [
.usesFontLeading,
.usesLineFragmentOrigin,
.usesDeviceMetrics])


let textLayer = CATextLayer()
textLayer.frame = .init(origin: .zero, size: size)
textLayer.contentsScale = 2 // for retina
textLayer.isWrapped = true // for multiple lines
textLayer.string = attributedString

然后可以将 CATextLayer添加到任何 NSView/UIView

MacOS

let view = NSView()
view.wantsLayer = true
view.layer?.addSublayer(textLayer)

IOS

let view = UIView()
view.layer.addSublayer(textLayer)

原始答案2021年2月

这里的许多答案是伟大的,大卫里斯很好地总结了 选择

但是,有时当有特殊字符或多个空白时,大小似乎总是错误的。

一个不工作的字符串示例(对我来说) :

"hello    .   .  world"

我发现将 NSAttributedStringKern设置为 1有助于返回正确的大小。

像这样:

NSAttributedString(
string: "some string",
attributes: [
.font: NSFont.preferredFont(forTextStyle: .body),
.kern: 1])

我在计算 NSTextField的高度时遇到了问题。我尝试的任何方法总是返回太小的值。
对我来说,问题在于,由于某种原因,NSTextFieldattributedStringValue属性从未包含我通过 Interface Builder 设置的任何属性。如果我没有以编程方式设置属性化字符串,它实际上根本不包含任何属性。连个字体都没有。这就是为什么所有的高度计算都搞砸了。

为了让它工作,我为 NSTextField创建了一个 Category,它实现了一个自定义函数来获得正确的属性化字符串。

下面是 Category的实现文件:

//
// --------------------------------------------------------------------------
// NSTextField+Additions.m
// Created for Mac Mouse Fix (https://github.com/noah-nuebling/mac-mouse-fix)
// Created by Noah Nuebling in 2021
// Licensed under MIT
// --------------------------------------------------------------------------
//


#import "NSTextField+Additions.h"


@implementation NSTextField (Additions)


// Copy paste template for adding attributes to an attributed string. Contains all possible attributes


//    [str addAttributes:@{
//        NSFontAttributeName:                NSNull.null,
//        NSParagraphStyleAttributeName:      NSNull.null,
//        NSForegroundColorAttributeName:     NSNull.null,
//        NSBackgroundColorAttributeName:     NSNull.null,
//        NSLigatureAttributeName:            NSNull.null,
//        NSKernAttributeName:                NSNull.null,
//        NSStrikethroughStyleAttributeName:  NSNull.null,
//        NSUnderlineStyleAttributeName:      NSNull.null,
//        NSStrokeColorAttributeName:         NSNull.null,
//        NSStrokeWidthAttributeName:         NSNull.null,
//        NSShadowAttributeName:              NSNull.null,
//        NSTextEffectAttributeName:          NSNull.null,
//        NSAttachmentAttributeName:          NSNull.null,
//        NSLinkAttributeName:                NSNull.null,
//        NSBaselineOffsetAttributeName:      NSNull.null,
//        NSUnderlineColorAttributeName:      NSNull.null,
//        NSStrikethroughColorAttributeName:  NSNull.null,
//        NSObliquenessAttributeName:         NSNull.null,
//        NSExpansionAttributeName:           NSNull.null,
//        NSWritingDirectionAttributeName:    NSNull.null,
//        NSVerticalGlyphFormAttributeName:   NSNull.null,
//    } range:NSMakeRange(0, str.length)];


/// In my testing NSTextField.attributedStringValue actually returned a string without _any_ attributes. Not even a font or anything.
/// This lead to issues when trying to calculate the fitting height for a certain width of the NSTextField.
/// This function takes some of the properties of the NSTextField and returns an NSAttributed string based on those.
/// I'm not sure this is perfect, but the returned attributed string describes the way that the text of the NSTextField is rendered close enough to be usable for my height calculations
- (NSAttributedString *)effectiveAttributedStringValue {
    

NSMutableAttributedString *str = self.attributedStringValue.mutableCopy;


// Create paragraph style from NSTextField properties
    

// Not sure if we're setting these properties correctly, and there could be more properties we should be setting
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
paragraphStyle.alignment = self.alignment;
paragraphStyle.baseWritingDirection = self.baseWritingDirection;
paragraphStyle.lineBreakMode = self.lineBreakMode;
paragraphStyle.allowsDefaultTighteningForTruncation = self.allowsDefaultTighteningForTruncation;
if (@available(macOS 10.15, *)) paragraphStyle.lineBreakStrategy = self.lineBreakStrategy;
    

// Add attributes to AttributedString based on NSTextField properties
     

[str addAttributes:@{
NSFontAttributeName:                self.font,
NSParagraphStyleAttributeName:      paragraphStyle,
NSForegroundColorAttributeName:     self.textColor,
NSBackgroundColorAttributeName:     self.backgroundColor,
//        NSLigatureAttributeName:            NSNull.null,
//        NSKernAttributeName:                NSNull.null,
//        NSStrikethroughStyleAttributeName:  NSNull.null,
//        NSUnderlineStyleAttributeName:      NSNull.null,
//        NSStrokeColorAttributeName:         NSNull.null,
//        NSStrokeWidthAttributeName:         NSNull.null,
//        NSShadowAttributeName:              NSNull.null, //self.shadow,
//        NSTextEffectAttributeName:          NSNull.null,
//        NSAttachmentAttributeName:          NSNull.null,
//        NSLinkAttributeName:                NSNull.null,
//        NSBaselineOffsetAttributeName:      NSNull.null, //self.baselineOffsetFromBottom,
//        NSUnderlineColorAttributeName:      NSNull.null,
//        NSStrikethroughColorAttributeName:  NSNull.null,
//        NSObliquenessAttributeName:         NSNull.null,
//        NSExpansionAttributeName:           NSNull.null,
//        NSWritingDirectionAttributeName:    NSNull.null, //self.baseWritingDirection,
//        NSVerticalGlyphFormAttributeName:   NSNull.null,
} range:NSMakeRange(0, str.length)];
    

// return NSAttributedString
    

return str;
    

}


@end



随机旁注

  • 我在这个帖子中读到的一些关于人们使用 UILabel的问题听起来很像是有关联的。
  • 我最终决定使用 NSTextView over NSTextField,因为它用于获得属性化字符串工作的方法是开箱即用的,而使用 NSTextField作为可点击的链接也是完全拙劣的。我的印象是,除了最基本的用例之外,NSTextField只是一个应该避免的错误混乱。

我有时在计算限制 Rect 的高度时会遇到问题,特别是在段落和断行方面。添加 .usesDeviceMetrics作为参数起到了作用。现在看起来在所有情况下都很好。

extension NSAttributedString {


func heightWithWidth(_ width: CGFloat) -> CGFloat {


let constraints = CGSize(width: width, height: .infinity)


let bounding = self.boundingRect(with: constraints, options: [.usesLineFragmentOrigin, .usesFontLeading, .usesDeviceMetrics], context: nil)
return bounding.height


}
}