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

Iphone CTFramesetterSuggestFrameSizeWithConstraints有时返回不正确的大小?,iphone,xcode,core-graphics,core-text,Iphone,Xcode,Core Graphics,Core Text,在下面的代码中,CTFramesetterSuggestFrameSizeWithConstraints有时返回一个CGSize,其高度不足以包含传递到其中的所有文本。我确实看过。但是在我的例子中,文本框的宽度需要是恒定的。是否有其他/更好的方法来计算属性字符串的正确高度?谢谢 CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString(attributedString); CGSize tmpSize = CTF

在下面的代码中,
CTFramesetterSuggestFrameSizeWithConstraints
有时返回一个
CGSize
,其高度不足以包含传递到其中的所有文本。我确实看过。但是在我的例子中,文本框的宽度需要是恒定的。是否有其他/更好的方法来计算属性字符串的正确高度?谢谢

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

CTFramesetterSuggestFrameSizeWithConstraints()
已断开。不久前我在这上面写了个错误。您可以选择使用路径足够高的
CTFramesetterCreateFrame()
。然后你可以测量你得到的CTFrame的矩形。请注意,不能使用
CGFLOAT\u MAX
作为高度,因为CoreText使用iPhone中的翻转坐标系,并将其文本定位在框的“顶部”。这意味着如果您使用
CGFLOAT\u MAX
,您将没有足够的精度来实际显示长方体的高度。我建议使用10000之类的高度作为高度,因为它比屏幕本身高10倍,并且为生成的矩形提供了足够的精度。如果需要布局更高的文本,可以对文本的每个部分进行多次布局(您可以向CTFrameRef询问它能够布局的原始字符串中的范围)。

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

我终于发现了。。当使用CTFramesetterSuggestFrameSizeWithConstraints返回CGSize来绘制文本时,该大小被认为不足以(有时)绘制整个文本,因此不会绘制最后一行。。我只是在size.height上加了1作为回报,现在看起来是这样

大概是这样的:


suggestedSize=CGSizeMake(suggestedSize.width、suggestedSize.height+1)

感谢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)

在解决这个问题的过程中,我从几张海报上找到了很多不同的答案,我为正确的文本大小这一大问题找到了解决方案,对于我来说,
CTFramesetterSuggestFrameSizeWithConstraints
无法正常工作,因此我们需要使用
CTFramesetterCreateFrame
,然后测量该帧的大小,(这是
UIFont
扩展名)这是swift 100%

参考资料

然后我们测量我们的CTFrame

static func measure(frame:CTFrame) ->CGSize {

        let lines = CTFrameGetLines(frame)
        let numOflines = CFArrayGetCount(lines)
        var maxWidth : Double = 0

        for index in 0..<numOflines {
            let line : CTLine = unsafeBitCast(CFArrayGetValueAtIndex(lines, index), to: CTLine.self)
            var ascent : CGFloat = 0
            var descent : CGFloat = 0
            var leading : CGFloat = 0
            let width = CTLineGetTypographicBounds(line, &ascent, &descent, &leading)

            if(width > maxWidth) {
                maxWidth = width
            }
        }

        var ascent : CGFloat = 0
        var descent : CGFloat = 0
        var leading : CGFloat = 0


        CTLineGetTypographicBounds(unsafeBitCast(CFArrayGetValueAtIndex(lines, 0), to: CTLine.self), &ascent, &descent, &leading)
        let firstLineHeight = ascent + descent + leading

        CTLineGetTypographicBounds(unsafeBitCast(CFArrayGetValueAtIndex(lines, numOflines - 1), to: CTLine.self), &ascent, &descent, &leading)
        let lastLineHeight = ascent + descent + leading

        var firstLineOrigin : CGPoint = CGPoint(x: 0, y: 0)
        CTFrameGetLineOrigins(frame, CFRangeMake(0, 1), &firstLineOrigin);

        var lastLineOrigin : CGPoint = CGPoint(x: 0, y: 0)
        CTFrameGetLineOrigins(frame, CFRangeMake(numOflines - 1, 1), &lastLineOrigin);

        let textHeight = abs(firstLineOrigin.y - lastLineOrigin.y) + firstLineHeight + lastLineHeight

        return CGSize(width: maxWidth, height: Double(textHeight))
    }
static func measure(帧:CTFrame)->CGSize{
let line=CTFrameGetLines(帧)
设numOflines=CFArrayGetCount(行)
变量maxWidth:Double=0
对于0..maxWidth中的索引){
最大宽度=宽度
}
}
var上升:CGFloat=0
变量下降:CGFloat=0
变量前导:CGFloat=0
CtlineGettyPrographicBounds(非安全比特广播(CFArrayGetValueAtIndex(第0行)、to:CTLine.self)、上升、下降和领先)
让一线高度=上升+下降+领先
CtlineGettyPrographicBounds(非安全比特广播(CFArrayGetValueAtIndex(lines,numOflines-1),to:CTLine.self),上升、下降和领先)
让lastLineHeight=上升+下降+领先
变量firstLineOrigin:CGPoint=CGPoint(x:0,y:0)
CTFrameGetLineOrigins(frame、CFRangeMake(0、1)和FirstLineOrigins);
var lastLineOrigin:CGPoint=CGPoint(x:0,y:0)
CTFrameGetLineOrigins(frame、CFRangeMake(numOflines-1、1)和LastLineOrigins);
设textHeight=abs(firstLineOrigin.y-lastLineOrigin.y)+firstLineHeight+lastLineHeight
返回CGSize(宽度:maxWidth,高度:Double(文本高度))
}

这让我抓狂,上面的任何解决方案都不适用于我计算多页上长字符串的布局以及完全截断的行的返回范围值。阅读API注释后,“stringRange”的CTFramesetterSuggestFrameSizeWithConstraints中的参数为:

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


将字符串范围设置为“CFRangeMake(currentLocation,0)”而不是“CFRangeMake(currentLocation,string.length)”,这一切都可以正常工作。

CGFLOAT_MAX在当前情况下确实是个问题,将值设置为100.000似乎效果很好。但奇怪的是,我只在iOS5上遇到这个问题。@GregoryM:是的,这就是为什么我建议使用类似10000的值,这是我使用的值(大到足够有用,小到仍然允许小数部分有足够的精度).这是一个古老的答案,但如何从CTFrame获得CGRect或高度?@AndrewPark:有点复杂。您必须计算帧中最后一行的rect,并计算该行底部和您给帧设置者的空间顶部之间的差值,以确定它需要多少空间。@KevinBallard感谢您重新讨论这个问题和澄清!那么,当属性字符串中有多个字体时,这是如何实现的呢?您可以添加段落属性
static func measure(frame:CTFrame) ->CGSize {

        let lines = CTFrameGetLines(frame)
        let numOflines = CFArrayGetCount(lines)
        var maxWidth : Double = 0

        for index in 0..<numOflines {
            let line : CTLine = unsafeBitCast(CFArrayGetValueAtIndex(lines, index), to: CTLine.self)
            var ascent : CGFloat = 0
            var descent : CGFloat = 0
            var leading : CGFloat = 0
            let width = CTLineGetTypographicBounds(line, &ascent, &descent, &leading)

            if(width > maxWidth) {
                maxWidth = width
            }
        }

        var ascent : CGFloat = 0
        var descent : CGFloat = 0
        var leading : CGFloat = 0


        CTLineGetTypographicBounds(unsafeBitCast(CFArrayGetValueAtIndex(lines, 0), to: CTLine.self), &ascent, &descent, &leading)
        let firstLineHeight = ascent + descent + leading

        CTLineGetTypographicBounds(unsafeBitCast(CFArrayGetValueAtIndex(lines, numOflines - 1), to: CTLine.self), &ascent, &descent, &leading)
        let lastLineHeight = ascent + descent + leading

        var firstLineOrigin : CGPoint = CGPoint(x: 0, y: 0)
        CTFrameGetLineOrigins(frame, CFRangeMake(0, 1), &firstLineOrigin);

        var lastLineOrigin : CGPoint = CGPoint(x: 0, y: 0)
        CTFrameGetLineOrigins(frame, CFRangeMake(numOflines - 1, 1), &lastLineOrigin);

        let textHeight = abs(firstLineOrigin.y - lastLineOrigin.y) + firstLineHeight + lastLineHeight

        return CGSize(width: maxWidth, height: Double(textHeight))
    }