Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I draw a string with no margins in a view?

I'm having trouble drawing an NSAttributedString with no margins around it in its view. Here's my code:

NSDictionary *attributes = @{NSFontAttributeName: [UIFont systemFontOfSize:72.0f]};
NSAttributedString *string = [[NSAttributedString alloc] initWithString:@"Hello"
                                                             attributes:attributes];
[string drawWithRect:rect
             options:NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesDeviceMetrics
             context:nil];

This leads to the behavior below:

String drawn with margins

Note the margins at the left and top.

  1. How can I avoid these margins and make the text exactly meet the edges of the containing view? What I want is for the actual drawn text to meet the edges of the view, that is, the topmost pixel drawn in any glyph should be at the top of the view, and the leftmost pixel drawn in any glyph is at the left side of the view.
  2. Assuming 1 is possible, is there a way I can get the actual width and height of the drawn text so that I can calculate font size and kerning to make the text meet the bottom and right edges as well?

I realize that there may be ways to align text without making it meet the edges of the view, but making it meet the edges of the view will allow me to work with the view intuitively using autolayout and so on.

I do not care about the behavior when the string contains leading or trailing whitespace.

If this is not possible with NSAttributedString, are there other ways to get this behavior that you'd recommend?

To clarify, here's what I want to see for number 1.

Desired behavior

like image 981
Luke Avatar asked May 31 '15 18:05

Luke


2 Answers

You can get the height,width vector values using CoreText:

double fWidth = CTLineGetTypographicBounds(line, &ascent, &descent, &leading);
size_t width = (size_t)ceilf(fWidth);
size_t height = (size_t)ceilf(ascent + descent + leading);

so you can get the rect. But the top margin is not wrong

like image 172
maquanhong Avatar answered Nov 07 '22 20:11

maquanhong


Use CoreText, CTLineGetTypographicBounds() is probably what you are looking for. I haven't used this myself. The following code demonstrate the idea (drawing "Hello" to a custom UIView with no top/left margin).

override func drawRect(rect: CGRect) {
    let context = UIGraphicsGetCurrentContext()
    UIGraphicsPushContext(context)

    let viewHeight = bounds.size.height

    let attributedString = NSAttributedString(string: "Hello")
    let line = CTLineCreateWithAttributedString(attributedString)
    var ascent: CGFloat = 0.0
    let width = CTLineGetTypographicBounds(line, &ascent, nil, nil)

    CGContextSaveGState(context)

    CGContextScaleCTM(context, 1.0, -1.0);
    CGContextTranslateCTM(context, 0, -viewHeight)
    CGContextSetTextPosition(context, 0, viewHeight - ascent)
    CTLineDraw(line, context)

    CGContextRestoreGState(context)

    UIGraphicsPopContext()
}
like image 40
Joe Smith Avatar answered Nov 07 '22 21:11

Joe Smith