Developer's Blog

より豊かな表現のために、3つのステップで実装する Core Text

こんにちは、開発担当の眞弓です。

テキストを表示するとき、通常は UILabel などを使いますがときどき「凝った文字列を使いたい」ということはないでしょうか? そんなときのために、今回は Core Text の基本的な使い方について書いてみようと思います。

人によってやり方は違うと思いますが、ちょっとしたことに使う場合に私がやっててわかりやすかった方法を紹介します。
位置を指定するやりかたはイメージしにくいので、文字列単位で扱えるやり方で実装をしてみました。
以下のような感じで、「属性付き文字列を作って、その文字列を付け足していって、最後にまとめて表示」というやり方です。

  • 1 : 文字列を作成
  • 2 : フレームを作成
  • 3 : DrawRect
 

 

1 : 文字列を作成

属性付き文字列は以下のような感じで作っていきます。

atrString = [[NSMutableAttributedString alloc] init];
- (void) setSampleText {
    // ( 以下と同様なので 1 ~ 4 は省略 )

    NSMutableDictionary *atrDict5 = [[NSMutableDictionary alloc] init];
    UIFont *f5 = [UIFont systemFontOfSize:28];
    CTFontRef ctFont5 = CTFontCreateWithName((CFStringRef) f5.fontName, f5.pointSize, NULL);
    [atrDict5 setObject:(id)ctFont5 forKey:(NSString *)kCTFontAttributeName];
    CFRelease(ctFont5); 
    [atrDict5 setObject:(id)[UIColor blueColor].CGColor forKey:(NSString *)kCTForegroundColorAttributeName];
    [atrDict5 setObject:[NSNumber numberWithInt:(int)kCTUnderlineStyleSingle|kCTUnderlinePatternDot] forKey:(NSString *)kCTUnderlineStyleAttributeName];
    [atrDict5 setObject:[NSNumber numberWithFloat:3.0] forKey:(NSString *)kCTStrokeWidthAttributeName];

    NSMutableAttributedString *atrString5 = [[NSAttributedString alloc] initWithString:@"ストローク幅を変えたりできます。" attributes:atrDict5];	
    [atrString appendAttributedString:atrString5];

    [atrDict5 release];
    [atrString5 release];


    [self updateText];
}

細かく見ていきますと、

  • 文字サイズ設定
  • UIFont *f5 = [UIFont systemFontOfSize:28];
    CTFontRef ctFont5 = CTFontCreateWithName((CFStringRef) f5.fontName, f5.pointSize, NULL);
    [atrDict5 setObject:(id)ctFont5 forKey:(NSString *)kCTFontAttributeName];
    CFRelease(ctFont5); 
    
     
  • 文字色設定
  • [atrDict5 setObject:(id)[UIColor blueColor].CGColor forKey:(NSString *)kCTForegroundColorAttributeName];
    
     
  • アンダーライン設定
  • [atrDict5 setObject:[NSNumber numberWithInt:(int)kCTUnderlineStyleSingle|kCTUnderlinePatternDot] forKey:(NSString *)kCTUnderlineStyleAttributeName];
    
     
  • ストローク幅設定
  • [atrDict5 setObject:[NSNumber numberWithFloat:3.0] forKey:(NSString *)kCTStrokeWidthAttributeName];
    

となります。

 

2 : フレームを作成

文字列が作成できたら、その文字列から描画のためのフレームを作成します。

CTFramesetterRef    ctFrameSetter;
CTFrameRef    ctFrame;
CGMutablePathRef    cgPath;
-(void) updateText {
    if (ctFrame) {
        CFRelease(ctFrame);
        ctFrame = NULL;
    }
    CGPathRelease(cgPath);
    cgPath = NULL;

    if (!atrString || [atrString length] == 0) return;

    CTFramesetterRef frs = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)atrString);
    cgPath = CGPathCreateMutable();
    CGPathAddRect(cgPath, NULL, self.bounds);
    ctFrame = CTFramesetterCreateFrame(frs, CFRangeMake(0, 0), cgPath, NULL);
    CFRelease(frs);
	
    [self setNeedsDisplay];
}
 

3 : DrawRect

フレームが作成できたら、そのフレームをもとに描画をします。描画のやり方はいろいろありますが、ラインで描画する方が少し凝ったことをするときにやりやすい気がします。

- (void)drawRect:(CGRect)rect;
{
    CGAffineTransform cgTransform = CGAffineTransformMake(1, 0, 0, -1, 0, CGRectGetHeight(self.bounds));
    CGContextRef cgContext = UIGraphicsGetCurrentContext();
    CGContextConcatCTM(cgContext, cgTransform);
	
    NSMutableArray *lines = [NSMutableArray arrayWithArray:(id)(id)CTFrameGetLines(ctFrame)];
    CGPoint *origins = malloc(sizeof(CGPoint) * [lines count]);
    CTFrameGetLineOrigins(ctFrame, CFRangeMake(0, 0), origins);
    for (int i = 0; i < [lines count]; ++i) {
        CGPoint origin = origins[i];		
        CGContextSetTextPosition(cgContext, origin.x ,origin.y);
        CTLineRef line = (CTLineRef)[lines objectAtIndex:i];
        CTLineDraw(line, cgContext);		
    }
    free(origins);
}
 

最後に

「UIWebView を使えばいいんじゃないの?」っていう意見もあると思いますが、UIWebView だと描画タイミングの問題で UITableView で使うときなどではスクロールが止まってからしか表示されません。そういう描画タイミングをコントロールしたいときには Core Text の出番かと思います。

Copyright © 2019 Fenrir Inc. All rights reserved.