How to use NSString drawInRect to center text?

| | August 6, 2015

How can I draw a NSString centered within a NSRect?

I’ve started off with: (an extract from the drawRect method of my custom view)

NSString* theString = ...
[theString drawInRect:theRect withAttributes:0];
[theString release];

Now I’m assuming I need to set up some attributes. I’ve had a look through Apple’s Cocoa documentation, but it’s a bit overwhelming and can’t find anything for how to add paragraph styles to the attributes.

Also, I can only find horizontal alignment, what about vertical alignment?

8 Responses to “How to use NSString drawInRect to center text?”

  1. Martin Pilkington on November 30, -0001 @ 12:00 AM

    Vertical alignment you’ll have to do yourself ((height of view + height of string)/2). Horizontal alignment you can do with:

    NSMutableParagraphStyle *style = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
    style.alignment = NSCenterTextAlignment;
    NSDictionary *attr = [NSDictionary dictionaryWithObject:style forKey:NSParagraphStyleAttributeName];
    [myString drawInRect:someRect withAttributes:attr];
    
  2. Nikolay Klimchuk on November 30, -0001 @ 12:00 AM

    This works for me for horizontal alignment

    [textX drawInRect:theRect 
             withFont:font 
        lineBreakMode:UILineBreakModeClip 
            alignment:UITextAlignmentCenter];
    
  3. The correct answer is:

    -drawInRect:withFont:lineBreakMode:alignment:

    I also created a small category for vertical alignment. If you like to use it, go ahead :)

    // NSString+NSVerticalAlign.h
    typedef enum {
        NSVerticalTextAlignmentTop,
        NSVerticalTextAlignmentMiddle,
        NSVerticalTextAlignmentBottom
    } NSVerticalTextAlignment;
    
    @interface NSString (VerticalAlign)
    - (CGSize)drawInRect:(CGRect)rect withFont:(UIFont *)font verticalAlignment:(NSVerticalTextAlignment)vAlign;
    - (CGSize)drawInRect:(CGRect)rect withFont:(UIFont *)font lineBreakMode:(NSLineBreakMode)lineBreakMode verticalAlignment:(NSVerticalTextAlignment)vAlign;
    - (CGSize)drawInRect:(CGRect)rect withFont:(UIFont *)font lineBreakMode:(NSLineBreakMode)lineBreakMode alignment:(NSTextAlignment)alignment verticalAlignment:(NSVerticalTextAlignment)vAlign;
    @end
    
    
    // NSString+NSVerticalAlign.m
    #import "NSString+NSVerticalAlign.h"
    
    @implementation NSString (VerticalAlign)
    - (CGSize)drawInRect:(CGRect)rect withFont:(UIFont *)font verticalAlignment:(NSVerticalTextAlignment)vAlign {
    switch (vAlign) {
        case NSVerticalTextAlignmentTop:
            break;
    
        case NSVerticalTextAlignmentMiddle:
            rect.origin.y = rect.origin.y + ((rect.size.height - font.pointSize) / 2);
            break;
    
        case NSVerticalTextAlignmentBottom:
            rect.origin.y = rect.origin.y + rect.size.height - font.pointSize;
            break;
        }
        return [self drawInRect:rect withFont:font];
    }
    - (CGSize)drawInRect:(CGRect)rect withFont:(UIFont *)font lineBreakMode:(NSLineBreakMode)lineBreakMode verticalAlignment:(NSVerticalTextAlignment)vAlign {
    switch (vAlign) {
        case NSVerticalTextAlignmentTop:
            break;
    
        case NSVerticalTextAlignmentMiddle:
            rect.origin.y = rect.origin.y + ((rect.size.height - font.pointSize) / 2);
            break;
    
        case NSVerticalTextAlignmentBottom:
            rect.origin.y = rect.origin.y + rect.size.height - font.pointSize;
            break;
        }   
        return [self drawInRect:rect withFont:font lineBreakMode:lineBreakMode];
    }
    - (CGSize)drawInRect:(CGRect)rect withFont:(UIFont *)font lineBreakMode:(NSLineBreakMode)lineBreakMode alignment:(NSTextAlignment)alignment verticalAlignment:(NSVerticalTextAlignment)vAlign {
    switch (vAlign) {
        case NSVerticalTextAlignmentTop:
            break;
    
        case NSVerticalTextAlignmentMiddle:
            rect.origin.y = rect.origin.y + ((rect.size.height - font.pointSize) / 2);
            break;
    
        case NSVerticalTextAlignmentBottom:
            rect.origin.y = rect.origin.y + rect.size.height - font.pointSize;
            break;
        }   
        return [self drawInRect:rect withFont:font lineBreakMode:lineBreakMode alignment:alignment];
    }
    @end
    
  4. matthewwithanm on November 30, -0001 @ 12:00 AM

    Martins answer is pretty close, but it has a few small errors. Try this:

    NSMutableParagraphStyle* style = [[NSMutableParagraphStyle alloc] init];
    [style setAlignment:NSCenterTextAlignment];
    NSDictionary *attr = 
      [NSDictionary dictionaryWithObject:style 
                                  forKey:NSParagraphStyleAttributeName];
    [myString drawInRect:someRect withAttributes:attr];
    [style release];
    

    You’ll have to create a new NSMutableParagraphStyle (instead of using the default paragraph style as Martin suggested) because [NSMutableParagraphStyle defaultParagraphStyle] returns an NSParagraphStyle, which doesn’t have the setAlignment method. Also, you don’t need the string @"NSParagraphStyleAttributeName"—just NSParagraphStyleAttributeName.

  5. [NSMutableParagraphStyle defaultParagraphStyle] won’t work use:

    [NSMutableParagraphStyle new]

    also, it appears horizontal alignment only works for drawInRect, not drawAtPoint (ask me how I know :-)

  6. This works for me:

        CGRect viewRect = CGRectMake(x, y, w, h);
        UIFont* font = [UIFont systemFontOfSize:15];
        CGSize size = [nsText sizeWithFont:font
                         constrainedToSize:viewRect.size
                             lineBreakMode:(UILineBreakModeWordWrap)];  
        float x_pos = (viewRect.size.width - size.width) / 2; 
        float y_pos = (viewRect.size.height - size.height) /2; 
        [someText drawAtPoint:CGPointMake(viewRect.origin.x + x_pos, viewRect.origin.y + y_pos) withFont:font];
    
  7. The following snippet is useful for drawing a center text string using an Annotation custom image as a reference:

    CustomAnnotation.h

    @interface CustomAnnotation : MKAnnotationView
    [...]
    

    CustomAnnotation.m

    [...]        
           - (void)drawRect:(CGRect)rect
            {
                ClusterAnnotation *associatedAnnotation = (CustomAnnotation *)self.annotation;
                if (associatedAnnotation != nil)
                {
                    CGContextRef context = UIGraphicsGetCurrentContext();
    
                    NSString *imageName = @"custom_image.png";                
                    CGRect contextRect = CGRectMake(0, 0, 42.0, 42.0);
                    CGFloat fontSize = 14.0;        
    
                    [[UIImage imageNamed:imageName] drawInRect:contextRect];
    
                    NSInteger myIntegerValue = [associatedAnnotation.dataObject.myIntegerValue integerValue];
                    NSString *myStringText = [NSString stringWithFormat:@"%d", myIntegerValue];
    
                    UIFont *font = [UIFont fontWithName:@"Helvetica-Bold" size:fontSize];
    
                    CGSize fontWidth = [myStringText sizeWithFont:font];
    
                    CGFloat yOffset = (contextRect.size.height - fontWidth.height) / 2.0;
                    CGFloat xOffset = (contextRect.size.width - fontWidth.width) / 2.0;        
    
                    CGPoint textPoint = CGPointMake(contextRect.origin.x + xOffset, contextRect.origin.y + yOffset);        
    
                    CGContextSetTextDrawingMode(context, kCGTextStroke);
    
                    CGContextSetLineWidth(context, fontSize/10);
                    CGContextSetStrokeColorWithColor(context, [[UIColor whiteColor] CGColor]);
                    [myStringText drawAtPoint:textPoint withFont:font];
    
                    CGContextSetTextDrawingMode(context, kCGTextFill);
                    CGContextSetFillColorWithColor(context, [[UIColor blackColor] CGColor]);
                    [myStringText drawAtPoint:textPoint withFont:font];
    
    
                }
            }
    
  8. Well, drawInRect is only good for basic text drawing (in other words, the system decides where to position your text) – often the only way to draw text positioned where you want is to simply calculate what point you want it at and use NSString’s drawAtPoint:withAttributes:.

    Also, NSString’s sizeWithAttributes is hugely useful in any positioning math you end up having to do for drawAtPoint.

    Good luck!

Leave a Reply