Drawing vertical text on iOS

I would like to draw an NSAttributedString (or NSString ) vertically on iOS. By "vertical" I mean:

Vertical text example

Apple's documentation says that the standard attribute on NSAttributedString is NSVerticalGlyphFormAttributeName , but unfortunately says:

"On iOS, horizontal text is always used, and setting a different value is undefined."

The same document also mentions NSTextLayoutSectionsAttribute , which appears to support NSTextLayoutOrientation , which allows NSTextLayoutOrientationHorizontal or NSTextLayoutOrientationVertical .

None of these 4 terms are taking any hits on SO at the moment. Just that lonely whistling sound.

However, I don’t know how to set it, regardless of whether it works with iOS or can be used for a vertical line in the style of [myAttributedString drawAtPoint: whereItOughtaGo] .

Thanks.

+6
source share
4 answers

If this is only one aligned text, as in your example, you can use various workarounds. I personally created a subclass of UILabel as follows:

 @implementation VerticalLabel - (id)initWithCoder:(NSCoder *)aDecoder { self = [super initWithCoder:aDecoder]; if (self) { self.numberOfLines = 0; } return self; } - (void)setText:(NSString *)text { NSMutableString *newString = [NSMutableString string]; for (int i = text.length-1; i >= 0; i--) { [newString appendFormat:@"%c\n", [text characterAtIndex:i]]; } super.text = newString; } @end 

Now you just need to replace:

 UILabel *lbl = [[UILabel alloc] init]; 

with:

 UILabel *lbl = [[VerticalLabel alloc] init]; 

to get vertical text.

+7
source

You can draw your vertical text using CoreText (to get glyphs) and CoreGraphics (to draw CGGlyphs). A simple example that concerns only one font attribute (handles the effective range for each attribute if you want to handle different fonts, font size, etc.)

 #import "NSAttributedString+VerticalDrawing.h" @implementation TestView - (void)drawRect:(CGRect)rect { NSAttributedString *aString = [[NSAttributedString alloc] initWithString:@"This is a vertical text" attributes:@{NSFontAttributeName:[UIFont boldSystemFontOfSize:14.]}]; [aString drawVerticalAtPoint:CGPointMake(10, 10)]; } @end @implementation NSAttributedString (VerticalDrawing) - (void)drawVerticalAtPoint:(CGPoint)location { UIFont *font = [[self attributesAtIndex:0 effectiveRange:NULL] objectForKey:NSFontAttributeName]; NSUInteger myLength = [self length]; CTFontRef ctfont = CTFontCreateWithName((CFStringRef)[font fontName], [font pointSize], NULL); CGGlyph *glyphs = malloc(sizeof(CGGlyph) * myLength); UniChar *characters = malloc(sizeof(UniChar) * myLength); CGSize *advances = malloc(sizeof(CGSize) * myLength); [[self string] getCharacters:characters range:NSMakeRange(0,myLength)]; CTFontGetGlyphsForCharacters(ctfont, characters, glyphs, myLength); CTFontGetAdvancesForGlyphs(ctfont, kCTFontHorizontalOrientation, glyphs, advances, myLength); free(characters); CGContextRef ctx = UIGraphicsGetCurrentContext(); CGFontRef cgfont = CTFontCopyGraphicsFont(ctfont, NULL); CGContextSetFont(ctx, cgfont); CGContextSetFontSize(ctx, CTFontGetSize(ctfont)); CGAffineTransform textMatrix = CGAffineTransformMakeTranslation(location.x, location.y); textMatrix = CGAffineTransformScale(textMatrix, 1, -1); CGContextSetTextMatrix(ctx, textMatrix); CGFloat lineHeight = CTFontGetAscent(ctfont) + CTFontGetDescent(ctfont) + CTFontGetLeading(ctfont); location.y = -CTFontGetAscent(ctfont); CGFloat maxAdvance = 0.; NSUInteger i; for (i = 0; i < myLength; i++) maxAdvance = MAX(maxAdvance, advances[i].width); for (i = 0; i < myLength; i++) { location.x = (maxAdvance - advances[i].width) * 0.5; CGContextShowGlyphsAtPositions(ctx, &glyphs[i], &location, 1); location.y -= lineHeight; } free(glyphs); free(advances); CGFontRelease(cgfont); CFRelease(ctfont); } @end 

sample

+5
source

To add to the other great answers, I played with NSTextLayoutSectionsAttribute and even subclassed NSTextContainer to override layoutOrientation to return NSTextLayoutOrientationVertical . This does not work. I see an NSATSTypesetter request for layout orientation, but rendering is still done in horizontal orientation. I assume this is a limitation of the main text; The documentation and header files indicate many times that the main text only supports horizontal layout orientation.

According to the documentation, the NSTextLayoutOrientationProvider protocol NSTextLayoutOrientationProvider provided only if the NSLayoutManager subclass wants to implement a different layout orientation.

+1
source

If you draw it using an attribute, you can create a paragraph attribute and set the left and right margins to 0 or very little. This results in a vertical display string when drawAtPoint is used.

0
source

All Articles