CTFramesetterSuggestFrameSizeWithConstraints有时返回不正确的大小?

时间:2010-07-30 19:05:58

标签: iphone xcode core-graphics core-text

在下面的代码中,CTFramesetterSuggestFrameSizeWithConstraints有时会返回一个CGSize,其高度不足以包含传递给它的所有文本。我确实看过this answer。但在我的情况下,文本框的宽度需要是恒定的。有没有其他/更好的方法来确定我的属性字符串的正确高度?谢谢!

CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString(attributedString);
CGSize tmpSize = CTFramesetterSuggestFrameSizeWithConstraints(framesetter, CFRangeMake(0,0), NULL, CGSizeMake(self.view.bounds.size.width, CGFLOAT_MAX), NULL); 
CGSize textBoxSize = CGSizeMake((int)tmpSize.width + 1, (int)tmpSize.height + 1);

6 个答案:

答案 0 :(得分:55)

CTFramesetterSuggestFrameSizeWithConstraints正常工作。您获得高度太短的原因是因为附加到属性字符串的默认段落样式中的前导。如果未将段落样式附加到字符串,则CoreText将返回呈现文本所需的高度,但行之间没有空格。这让我永远想通了。文档中没有任何内容可以解释它。我碰巧注意到我的高度很短,等于(行数x预期的领先)。要获得高度结果,您可以使用以下代码:

NSString  *text = @"This\nis\nsome\nmulti-line\nsample\ntext."
UIFont    *uiFont = [UIFont fontWithName:@"Helvetica" size:17.0];
CTFontRef ctFont = CTFontCreateWithName((CFStringRef) uiFont.fontName, uiFont.pointSize, NULL);

//  When you create an attributed string the default paragraph style has a leading 
//  of 0.0. Create a paragraph style that will set the line adjustment equal to
//  the leading value of the font.
CGFloat leading = uiFont.lineHeight - uiFont.ascender + uiFont.descender;
CTParagraphStyleSetting paragraphSettings[1] = { kCTParagraphStyleSpecifierLineSpacingAdjustment, sizeof (CGFloat), &leading };

CTParagraphStyleRef  paragraphStyle = CTParagraphStyleCreate(paragraphSettings, 1);
CFRange textRange = CFRangeMake(0, text.length);

//  Create an empty mutable string big enough to hold our test
CFMutableAttributedStringRef string = CFAttributedStringCreateMutable(kCFAllocatorDefault, text.length);

//  Inject our text into it
CFAttributedStringReplaceString(string, CFRangeMake(0, 0), (CFStringRef) text);

//  Apply our font and line spacing attributes over the span
CFAttributedStringSetAttribute(string, textRange, kCTFontAttributeName, ctFont);
CFAttributedStringSetAttribute(string, textRange, kCTParagraphStyleAttributeName, paragraphStyle);

CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString(string);
CFRange fitRange;

CGSize frameSize = CTFramesetterSuggestFrameSizeWithConstraints(framesetter, textRange, NULL, bounds, &fitRange);

CFRelease(framesetter);
CFRelease(string);

答案 1 :(得分:20)

CTFramesetterSuggestFrameSizeWithConstraints()被打破了。我一会儿就提出了一个错误。您可以选择使用CTFramesetterCreateFrame(),其路径足够高。然后你可以测量你得到的CTFrame的矩形。请注意,您不能使用CGFLOAT_MAX作为高度,因为CoreText使用iPhone的翻转坐标系统,并将其文本定位在框的“顶部”。这意味着如果你使用CGFLOAT_MAX,你就没有足够的精确度来实际告诉盒子的高度。我建议使用像10,000这样的高度,因为它比屏幕本身高10倍,并且为得到的矩形提供了足够的精度。如果你需要布置更高版本的文本,你可以为文本的每个部分多次执行此操作(您可以向CTFrameRef询问其能够布局的原始字符串中的范围)。

答案 2 :(得分:2)

感谢Chris DeSalvo的出色答案!最后结束了16个小时的调试。我在搞清楚Swift 3语法方面遇到了一些麻烦。所以分享设置段落样式的Swift 3版本。

let leading = uiFont.lineHeight - uiFont.ascender + uiFont.descender
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineSpacing = leading      
mutableAttributedString.addAttribute(NSParagraphStyleAttributeName, value: paragraphStyle, range: textRange)

答案 3 :(得分:0)

为解决这个问题,并从多个发帖人那里得到了许多不同的答案,我为所有可能的正确文本大小问题实施了一个解决方案,对我来说CTFramesetterSuggestFrameSizeWithConstraints工作不正常,因此我们需要使用{ {1}},然后测量该帧的大小(扩展名为CTFramesetterCreateFrame),即迅速100%

参考

CTFramesetterSuggestFrameSizeWithConstraints sometimes returns incorrect size?

How to get the real height of text drawn on a CTFrame

Using CFArrayGetValueAtIndex in Swift with UnsafePointer (AUPreset)

UIFont

然后我们测量CTFrame

func sizeOfString (string: String, constrainedToWidth width: Double) -> CGSize {
        let attributes = [NSAttributedString.Key.font:self]
        let attString = NSAttributedString(string: string,attributes: attributes)
        let framesetter = CTFramesetterCreateWithAttributedString(attString)

        let frame = CTFramesetterCreateFrame(framesetter,CFRange(location: 0,length: 0),CGPath.init(rect: CGRect(x: 0, y: 0, width: width, height: 10000), transform: nil),nil)

        return UIFont.measure(frame: frame)
    }

答案 4 :(得分:0)

这让我很恼火,上面的任何解决方案都不适合我计算多页长字符串的布局并返回带有完全截断行的范围值。阅读API说明后,CTFramesetterSuggestFrameSizeWithConstraints中'stringRange'的参数是这样的:

'将应用帧大小的字符串范围。字符串范围是用于创建框架设置器的字符串范围。如果范围的长度部分设置为 0,则框架设置器将继续添加行,直到文本或空间用完为止。'

将字符串范围设置为 'CFRangeMake(currentLocation, 0)' 而不是 'CFRangeMake(currentLocation, string.length)' 后,一切正常。

答案 5 :(得分:-6)

我终于发现..当使用CTFramesetterSuggestFrameSizeWithConstraints返回CGSize来绘制文本时,对于整个文本来说,大小被认为不够大(有时),所以最后一行不是画了..我只是为size.height添加1回来,现在看来是正确的。

这样的事情:

suggestedSize = CGSizeMake(suggestedSize.width,suggestedSize.height + 1);