在iOS上绘制垂直文本

时间:2014-03-05 11:05:32

标签: ios nsstring nsattributedstring

我想在iOS上垂直绘制NSAttributedString(或NSString)。通过“垂直”我的意思是:

Vertical text example

Apple's documentation表示NSAttributedString上的标准属性为NSVerticalGlyphFormAttributeName,但不幸的是,说:

  

“在iOS中,始终使用水平文本,并且未定义指定其他值。”

The same document还提到NSTextLayoutSectionsAttribute,它似乎支持NSTextLayoutOrientation,允许NSTextLayoutOrientationHorizontalNSTextLayoutOrientationVertical

这4个术语中的任何一个都不会在SO上获得任何点击。 只是那种孤独的吹口哨声

但是,我不知道如何设置它,是否适用于iOS,或者是否可以用[myAttributedString drawAtPoint: whereItOughtaGo]样式进行垂直字符串渲染。

感谢。

4 个答案:

答案 0 :(得分:7)

如果它只是您示例中的单行文本,则可以使用各种变通方法。我个人会创建一个UILabel子类,如下所示:

@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

现在你只需要替换:

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

使用:

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

获取垂直文字。

答案 1 :(得分:6)

您可以使用CoreText(获取字形)和CoreGraphics(绘制CGGlyphs)来绘制垂直文本。 一个简单的示例,它只处理一个字体属性(处理每个属性的有效范围,如果想要处理不同的字体,字体大小等...)

#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

答案 2 :(得分:1)

要添加其他优秀答案,我使用了NSTextLayoutSectionsAttribute,甚至将NSTextContainer子类化为覆盖layoutOrientation以返回NSTextLayoutOrientationVertical。它不起作用。我看到NSATSTypesetter查询布局方向,但渲染仍然是在水平方向上完成的。我想这是一个核心文本限制;文档和头文件多次声明Core Text中仅支持水平布局方向。

根据文档,仅当NSTextLayoutOrientationProvider的子类希望实现不同的布局方向时,才会提供NSLayoutManager协议。

答案 3 :(得分:0)

如果使用属性绘制它,则可以创建段落属性并将左右边距设置为0或非常小。当使用drawAtPoint时,这会产生垂直显示字符串。