boundingRectWithSize为NSAttributedString返回错误的大小

我试图得到一个归属string的矩形,但boundingRectWithSize调用不是我传入的大小,并返回一个单一的行高度,而不是一个很大的高度(这是一个长的string)的矩形。 我已经通过传递一个非常大的值的高度,也如下面的代码0,但返回的矩形总是相同的。

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

这是打破了,还是我需要做别的事情,让它返回包装文本rect?

看起来你没有提供正确的选项。 为了包装标签,至less要提供:

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

注意:如果原文宽度在300.f以下,就不会换行了,所以要确保绑定的大小是正确的,否则会得到错误的结果。

这种方法似乎有许多方面的错误。 就像你所说的那样,它并不考虑宽度的限制。 另一个,我看到它崩溃,因为它似乎假定所有的属性是NSObjecttypes(例如,它试图将_isDefaultFace传递给CTFontRef )。 当提供string绘图上下文时,它也会崩溃,因为它试图在后台添加一个无值属性给可变属性string。

我会鼓励你完全避免这种方法。 你可以直接使用核心文本估计string的大小,如果你可以处理为每个需要绘制的string创build一个framesetter的开销。 它也不是精确地遵守宽度约束,但根据我的经验,它似乎在几个像素内。

 CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef)attrString); CGSize targetSize = CGSizeMake(320, CGFLOAT_MAX); CGSize fitSize = CTFramesetterSuggestFrameSizeWithConstraints(framesetter, CFRangeMake(0, [attrString length]), NULL, targetSize, NULL); CFRelease(framesetter); 

出于某种原因,boundingRectWithSize总是返回错误的大小。 我想出了一个解决scheme。 有一个UItextView -sizeThatFits的方法,它返回文本集的正确大小。 因此,不是使用boundingRectWithSize,而是使用随机框架创build一个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创build,应用程序的运行时间内存将增加,如果我们不使用autoreleasepool。

埃德·麦克马纳斯(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]; 

矩形不会有正确的高度。 注意anotherString (附加到string )被初始化而没有属性字典。 这是anotherString的一个合法的初始值设定项,但是boundingRectWithSize:在这种情况下没有给出准确的大小。

经过长时间的调查,
- boundingRectWithSize函数仅为不间断的字符序列返回正确的大小! 如果string包含空格或其他内容(由苹果公司称之为“某些字形”),则不可能获得显示文本所需的实际大小的矩形!
我用字母replace了string中的空格,并立即得到正确的结果。

苹果在这里说: https : //developer.apple.com/documentation/foundation/nsstring/1524729-boundingrectwithsize

“这个方法返回string中字形的实际边界,一些字形(例如空格)允许重叠由传入大小指定的布局约束,所以在某些情况下,大小分量的宽度值返回的CGRect可能会超出size参数的宽度值。“

所以有必要find一些另外的方法来计算实际矩形…


经过漫长的调查过程解决scheme终于find 我不确定它会适用于所有与UITextView有关的情况,但主要和重要的事情被发现!

boundingRectWithSize函数以及CTFramesetterSuggestFrameSizeWithConstraints (以及许多其他方法)将在使用正确的矩形时计算大小和文本部分的正确性。 例如 – UITextView具有textView.bounds.size.width – 并且此值不是系统在UITextView绘制文本时使用的实际矩形。

我发现非常有趣的参数,并在代码中执行简单的计算:

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

而魔术作品 – 我的所有文字现在计算正确! 请享用!

如果你想通过截尾来获得包围盒, 这个问题可以帮助你。

 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]; 

@warrenm抱歉,说framesetter方法不适合我。

我得到this.This函数可以帮助我们确定一个给定的宽度在iPhone / Ipad SDK中的NSAttributedString的string范围所需的帧大小:

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

 - (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

我有同样的问题没有得到一个准确的大小使用这些技术,我已经改变了我的方法,使其工作。

我有一个很长的属性string,我一直试图适应一个滚动视图,以便它显示正确,而不被截断。 我所做的工作是使文本可靠工作,而不是将高度设置为一个约束,而是允许内在大小接pipe。 现在文本正确显示,而不被截断,我不必计算高度。

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

任何这些build议我都没有运气。 我的string包含unicode的要点,我怀疑他们在计算中造成悲伤。 我注意到UITextView正在处理图纸,所以我期待着利用它的计算。 我做了以下,这可能不是最佳的NSString绘图方法,但至less是准确的。 它也比初始化一个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); 

我发现首选解决scheme不处理换行符。

我发现这种方法适用于所有情况:

 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; 

Swift三版

 let string = "A great test string." let font = UIFont.systemFont(ofSize: 14) let attributes: [String: Any] = [NSFontAttributeName: 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 = (alert.alertDescription 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字符。

我有同样的问题,但我认识到,高度限制已被正确设置。 所以我做了以下几点:

 -(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; } 
 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格式不正确的情况。 实际上在标签上设置归档文本最终解决了问题。

我注意到的一件事是从(CGRect)boundingRectWithSize:(CGSize)size options:(NSStringDrawingOptions)options attributes:(NSDictionary *)attributes context:(NSStringDrawingContext *)context将会有一个更大的宽度我通过了。当发生这种情况我的string将被截断。 我解决了这个问题:

 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; 

我有点迟到游戏 – 但我一直在努力找出一种方法,find适合围绕一个属性的string,使像Finder编辑文件一样的聚焦环的边界框。 当string末尾有空格或string中有多个空格时,我所尝试过的所有内容都失败了。 boundingRectWithSizeCTFramesetterCreateWithAttributedString

使用一个NSLayoutManager下面的代码似乎在所有我发现迄今为止的情况下做的伎俩,并返回一个矩形,完全限制了string。 奖励:如果您select文本,select的边缘直到返回的矩形的边界。 下面的代码使用NSTextView的layoutManager。

 NSLayoutManager* layout = [self layoutManager]; NSTextContainer* container = [self textContainer]; CGRect focusRingFrame = [layout boundingRectForGlyphRange:NSMakeRange(0, [[self textStorage] length]) inTextContainer:container]; 

事实certificate,NSAttributedString的每一部分都必须有一个至less包含NSFontAttributeName和NSForegroundColorAttributeName集的字典集,如果你希望boundingRectWithSize实际工作的话!

我没有看到在任何地方logging。

  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这种方法将永远不会工作! 希望这可以帮助你! goog运气。

 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]; 

我想补充我的想法,因为我有完全相同的问题。

我使用的是UITextView因为它具有更好的文本alignment(certificate,当时在UILabel中没有),但为了“模拟”非交互式不可滚动的UILabel ,我将完全closures滚动,弹跳,和用户交互。

当然,问题是文本是dynamic的,而宽度是固定的,每次我设置新的文本值,高度应该重新计算。

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 ); } 

*上面的代码不是直接从我的源代码复制,我不得不调整它/从本文不需要的一堆其他东西清除它。 不要把它作为复制粘贴它将工作的代码。

明显的缺点是它有拨号和释放,为每个电话。

但是,优点是你可以避免依赖于如何boundingRectWithSize绘制文本,并计算它的大小和UITextView (或UILabel ,你也可以使用UITextViewUILabel )的文字绘图的实现之间的兼容性。 苹果可能有的任何“bug”都可以避免。

PS似乎你不应该需要这个“临时”的UITextView并可以直接从目标请求sizeThatFits ,但是这并不适用于我。 虽然逻辑会说它应该工作,临时UITextView分配/释放不需要,它没有。 但是,这个解决scheme完美地工作,我会设置任何文本。