NSString drawInRect:withAttributes: not correctly centered when using NSKernAttributeName

core-graphics, ios, kerning, nsstring, text-alignment

Solution

The kerning must be applied only to the first character of each kerning pair. If you want to display a string with kerning between all `n` characters then the kerning attribute must be set for the first `n-1` characters.

Instead of:

[text drawInRect:textRect withAttributes:@{NSKernAttributeName: @(self.kerning),
                                        NSForegroundColorAttributeName: textColor,
                                        NSFontAttributeName: font,
                                        NSParagraphStyleAttributeName: paragraphStyle}];

you have to create an attributed string so that you can set the kerning attribute for a specific range instead of the entire string:

NSMutableAttributedString *as = [[NSMutableAttributedString alloc]
                                 initWithString:text
                                 attributes:@{
                                              NSForegroundColorAttributeName: textColor,
                                              NSFontAttributeName: font,
                                              NSParagraphStyleAttributeName: paragraphStyle}];
[as addAttribute:NSKernAttributeName
           value:@(self.kerning)
           range:NSMakeRange(0, [text length] - 1)];

[as drawInRect:textRect];

Here the result for the string "1234" and kerning -4.0:

Problem

When I use `drawInRect:withAttributes:` and pass in both a paragraph style with `NSTextAlignmentCenter` and a non-zero value for `NSKernAttributeName`, the string does not get centered correctly. Am I doing something wrong or is this expected behavior? Is there a workaround? Screenshot: You can clearly see that top text is not centered correctly. My demo code: ``` - (void)drawRect:(CGRect)rect { // Drawing code UIFont *font = [UIFont systemFontOfSize:15.0]; [self drawString:@"88" inRect:rect font:font textColor:[UIColor blackColor]]; CGContextRef context = UIGraphicsGetCurrentContext(); CGContextSaveGState(context); CGContextAddRect(context, rect); CGContextStrokePath(context); CGContextRestoreGState(context); } - (void)drawString:(NSString *)text inRect:(CGRect)contextRect font:(UIFont *)font textColor:(UIColor *)textColor { CGFloat fontHeight = font.lineHeight; CGFloat yOffset = floorf((contextRect.size.height - fontHeight) / 2.0) + contextRect.origin.y; CGRect textRect = CGRectMake(contextRect.origin.x, yOffset, contextRect.size.width, fontHeight); NSMutableParagraphStyle *paragraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; paragraphStyle.lineBreakMode = NSLineBreakByClipping; paragraphStyle.alignment = NSTextAlignmentCenter; [text drawInRect:textRect withAttributes:@{NSKernAttributeName: @(self.kerning), NSForegroundColorAttributeName: textColor, NSFontAttributeName: font, NSParagraphStyleAttributeName: paragraphStyle}]; } ``` Thanks! Update, thanks to this comment, I added `NSBackgroundColorAttributeName: [UIColor greenColor]` to the attributes and got the following result:

Original source