Steve Folly
Steve Folly

Reputation: 8627

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)

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?

Upvotes: 44

Views: 68809

Answers (10)

Kiryl Bielašeŭski
Kiryl Bielašeŭski

Reputation: 2683

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

func drawInCenter(_ text: String, into rectangle: CGRect, with textFont: UIFont) {
    let paragraphStyle = NSMutableParagraphStyle()
    paragraphStyle.alignment = .center
    let rectangle = CGRect.zero
    paragraphStyle.minimumLineHeight = rectangle.height / 2 + textFont.lineHeight / 2
    let textAttributes = [NSAttributedString.Key.font: textFont,
                          NSAttributedString.Key.paragraphStyle: paragraphStyle] as [NSAttributedString.Key : Any]
    (text as NSString).draw(in: rectangle, withAttributes: textAttributes)
}

Upvotes: 3

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

Upvotes: 98

Kris
Kris

Reputation: 155

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

Upvotes: 4

Nikolay Klimchuk
Nikolay Klimchuk

Reputation: 345

This works for me for horizontal alignment

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

Upvotes: 27

Julian F. Weinert
Julian F. Weinert

Reputation: 7570

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

Upvotes: 1

matthewwithanm
matthewwithanm

Reputation: 4152

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.

Upvotes: 19

David Hoerl
David Hoerl

Reputation: 41682

[NSMutableParagraphStyle defaultParagraphStyle] won't work use:

[NSMutableParagraphStyle new]

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

Upvotes: 5

Don Grem
Don Grem

Reputation: 1267

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

Upvotes: 13

jalopezsuarez
jalopezsuarez

Reputation: 434

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


            }
        }

Upvotes: 0

Joel Levin
Joel Levin

Reputation: 2898

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!

Upvotes: 2

Related Questions