5

I have a custom UIView which is drawing an NSString via CoreText :

- (NSMutableAttributedString *)getAttributedString : (NSString *)displayText {
string = [[NSMutableAttributedString alloc]
                                     initWithString:displayText];

helvetica = CTFontCreateWithName(CFSTR("Helvetica"), 20.0, NULL);

[string addAttribute:(id)kCTFontAttributeName
               value:(__bridge id)helvetica
               range:NSMakeRange(0, [string length])];

return string;
}

- (void)drawRect:(CGRect)rect
{

CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString(
                                                                       (__bridge CFAttributedStringRef)string);
// left column form
leftColumnPath = CGPathCreateMutable();
CGPathAddRect(leftColumnPath, NULL,
              CGRectMake(0, 0,
                         self.bounds.size.width,
                         self.bounds.size.height));

// left column frame
textleftFrame = CTFramesetterCreateFrame(framesetter,
                                     CFRangeMake(0, 0),
                                     leftColumnPath, NULL);

if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
    // right column form
    rightColumnPath = CGPathCreateMutable();
    CGPathAddRect(rightColumnPath, NULL,
                  CGRectMake(self.bounds.size.width/2.0, 0,
                             self.bounds.size.width/2.0,
                             self.bounds.size.height));

    NSInteger rightColumStart = CTFrameGetVisibleStringRange(textleftFrame).length;

    // right column frame
    textrightFrame = CTFramesetterCreateFrame(framesetter,
                                          CFRangeMake(rightColumStart, 0),
                                          rightColumnPath,
                                          NULL);
}

// flip the coordinate system
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetTextMatrix(context, CGAffineTransformIdentity);
CGContextTranslateCTM(context, 0, self.bounds.size.height);
CGContextScaleCTM(context, 1.0, -1.0);

// draw
CTFrameDraw(textleftFrame, context);
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
    CTFrameDraw(textrightFrame, context);
}

// cleanup
CFRelease(textleftFrame);
CGPathRelease(leftColumnPath);
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
    CFRelease(textrightFrame);
    CGPathRelease(rightColumnPath);
}
CFRelease(framesetter);
CFRelease(helvetica);
CFRelease(helveticaBold);
}

In another class I am then trying to use boundingRectWithSize to calculate how long the view will be to display the text (I then later set a UIScrollView to match this) :

NSMutableAttributedString * attributedString = [textView getAttributedString:text];

    // Code here for iOS 7.0 - sizeWithFont is deprecated.
    CGRect textBoxSize = [attributedString boundingRectWithSize:CGSizeMake(315.f, CGFLOAT_MAX) options: (NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading) context:nil];

    textView.frame = CGRectMake(textView.frame.origin.x, pictureSpace, textBoxSize.size.width, textBoxSize.size.height);

The getAttributedString method is above. The problem is that textView is slightly too short in height and therefore cuts off the last line of so of text. Can anyone suggest what is wrong ?

Also, on a side note, why does the size in boundingRectWithSize have to be 315 (i.e slightly shorter than the screen width) rather than 320 in order to work ? At 320 the textView ends up slightly too wide for the screen.

Edit - this only seems to happen with certain fonts - e.g Verdana works fine. Does someone more knowledgable know if this something to do with glyphs ?

Thanks !

GuybrushThreepwood
  • 5,598
  • 9
  • 55
  • 113

2 Answers2

3

In the MMProgressHud project, the different sizes of the different components are computed using boundingRectWithSize:options:context:method and I had some issue using the HelveticaNeue-Light font but no one using the Verdana font.

I created a new category on NSString to make my own measurement method using Core-Text to have a regular behaviour. This is inspired by this post and this post.

#import "NSString+CustomMetrics.h"
#import<CoreText/CoreText.h>
@implementation NSString (CustomMetrics)

- (CGSize) boundingRectWithSize:(CGSize) bounds andFont:(UIFont*) uiFont
{

    CTFontRef ctFont = CTFontCreateWithName((CFStringRef) uiFont.fontName,uiFont.pointSize, NULL);

    CGFloat ascent = CTFontGetAscent(ctFont);
    CGFloat descent = CTFontGetDescent(ctFont);
    CGFloat leading = CTFontGetLeading(ctFont);

    if (leading < 0)
        leading = 0;

    leading = floor (leading + 0.5);

    CGFloat lineHeight = floor (ascent + 0.5) + floor (descent + 0.5) + leading;
    CGFloat  ascenderDelta = 0;
    if (leading > 0)
        ascenderDelta = 0;
    else
        ascenderDelta = floor (0.2 * lineHeight + 0.5);

    CGFloat defaultLineHeight = lineHeight + ascenderDelta;

    CTParagraphStyleSetting paragraphSettings[1] = { {kCTParagraphStyleSpecifierLineSpacingAdjustment, sizeof (CGFloat), &defaultLineHeight} };

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

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

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

    //  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);
    CFRelease(ctFont);

    return frameSize;

}
@end
Community
  • 1
  • 1
yageek
  • 4,115
  • 3
  • 30
  • 48
  • +1 yageek, I was struggling with same issue i.e. using Core Text I was able to draw text of some fonts but with not all and Finally your solution worked for me perfectly ! Thank you so much for sharing your Answer yageek. – Gaurav Singla Oct 12 '15 at 13:48
0

Ah - it seems CoreText spaces text differently to the boundingBox method : How does line spacing work in Core Text? (and why is it different from NSLayoutManager?)

Community
  • 1
  • 1
GuybrushThreepwood
  • 5,598
  • 9
  • 55
  • 113
  • So how does the different line spacing fit into your code to get the right size? I've been struggling with this sizing issue (using tableviews) for the past few months, too, and this is the first lead I've seen. – RL2000 Oct 23 '13 at 01:30
  • 1
    Try changing the font type - it only seems to happen with certain fonts. – GuybrushThreepwood Oct 23 '13 at 08:43
  • Cool, thanks for the info. I looked a little more and found this, which might help: http://stackoverflow.com/a/18818036/1275947 – RL2000 Oct 23 '13 at 13:02
  • @Ohnomycoco sometimes changing the font type is not an option :( – abbood Dec 18 '13 at 09:50
  • boundingBox replaced methods that actually worked. Frustrating if you have to target iOS 7 + 8. – Peter DeWeese Oct 31 '14 at 19:00