How to use NSString drawInRect to center text?

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)

  • How does an underscore in front of a variable in a cocoa objective-c class work?
  • Storyboard weird controller view frame origins
  • Adding Padding To The 'left' of a Text Field
  • Filter entire NSDictionaries out of NSArray based on multiple keys
  • How to determine if an OS X dylib was linked with -flat_namespace
  • How can I save the attributed string (text) into file (swift, cocoa)?
  • 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?

    10 Solutions Collect From Internet About “How to use NSString drawInRect to center text?”

    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 = NSTextAlignmentCenter;
    NSDictionary *attr = [NSDictionary dictionaryWithObject:style forKey:NSParagraphStyleAttributeName];
    [myString drawInRect:someRect withAttributes:attr];
    

    This works for me for horizontal alignment

    [textX drawInRect:theRect 
             withFont:font 
        lineBreakMode:UILineBreakModeClip 
            alignment:UITextAlignmentCenter];
    

    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.

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

    [NSMutableParagraphStyle defaultParagraphStyle] won’t work use:

    [NSMutableParagraphStyle new]

    also, it appears horizontal alignment only works for drawInRect, not drawAtPoint (ask me how I know πŸ™‚

    For anyone interested in an iOS7+ adaptation, drop this in an NSString category:

    - (void)drawVerticallyInRect:(CGRect)rect withFont:(UIFont *)font color:(UIColor *)color andAlignment:(NSTextAlignment)alignment
    {
        rect.origin.y = rect.origin.y + ((rect.size.height - [self sizeWithAttributes:@{NSFontAttributeName:font}].height) / 2);
    
        NSMutableParagraphStyle *style = [[NSMutableParagraphStyle alloc] init];
        [style setAlignment:alignment];
    
        [self drawInRect:rect withAttributes:@{
                                           NSFontAttributeName              : font,
                                           NSForegroundColorAttributeName   : color,
                                           NSParagraphStyleAttributeName    : style
                                           }];
    }
    

    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!

    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
    

    Only for iOS Swift 4,
    To center String within the rectangle using the method draw(in:withAttributes:)

    let textFont = UIFont.boldSystemFont(ofSize: )
    let paragraphStyle = NSMutableParagraphStyle()
    paragraphStyle.alignment = .center
    paragraphStyle.minimumLineHeight =  rectangle.height / 2 + textFont.lineHeight / 2
    let textAttributes = [
                NSAttributedStringKey.font: textFont,
                NSAttributedStringKey.paragraphStyle: paragraphStyle
                ] as [NSAttributedStringKey : Any]
    let text = "Text"
    (text as NSString).draw(in: rectangle, withAttributes: textAttributes)
    

    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];
    
    
                }
            }