非同期処理は、いったん保留してCore Textやるざます。
 縦書きね。

$テン*シー*シー-1

 それに合わせて、公開するするといいつつ保留になってた、するする詐欺のABプリントのプロジェクトの公開をするっすよ~。

ABプリント

 Core Data本を出したし、読んでくれた人へのサンプルの1つも兼ねてね。
 で、このABプリントは日本語の縦書きをやってるんですが、出したときはiOS 5全盛でiOS 5のCore Textはうまく縦書きができないとかいう都市伝説があったので、直接自前でCGContextShowGlyphs使って描いてたんですよ。

 それが…

$テン*シー*シー-2

 Deprecated:非推奨、いつ無くなっても責任は負わないよ~ん、サポートもしない。

 ごらんの有り様だよ…

 まあ、ぶっちゃけCGContextShowGlyphsAtPositionsを残してもらえてるんで、そのまま簡単にCGContextShowGlyphsをCGContextShowGlyphsAtPositionsに置き換えでもいいんですが、いい機会なのでCore Textを使ってみようかと思います。Use Core Text instead.とか書かれてるしな。

 iOS 6でもバグ技が話題になっているが、アラビア語の文字列を扱う時でiOS 6限定の不具合なので無視する。

 とにかく、いつもの感じでXcodeのFile→New→Project…メニューを選んでワンツースリーだ。
 今回はSingle View Applicationテンプレート使用ね。

$テン*シー*シー-3

 設定はこんな感じ。
 ここらへんは適当に。ついにARCデフォルトになりましたな。
 そのおかげでデバッグ本やドリル本の補足PDFを用意するはめになったのよ。近々サポートページにアップされる予定。

$テン*シー*シー-4
 そういえば、独自のクラスにはプリフィックスを3文字以上にしろってコーディング規約に明記されてたのを最近発見。EDじゃなくEDUくらいにするべきですな…

 で、作成されたEDViewControllerの画面に、Core Text実験用のビューを1つ貼付けます。
 これを
EDTextView

と名付け、新規に単独ファイルとしてプロジェクトに追加。
File→New→File…メニューを選んでワンツースリーだ。

$テン*シー*シー-5

 設定はこんな感じ。
 UIViewの派生クラスね。

$テン*シー*シー-6

 で、EDViewController.m側の-viewDidLoadメソッドでEDTextViewを1つ作成してEDViewControllerの管理するビュー:self.viewに貼付ける。
 これで準備派完了。

ブランチ:ex0 ブランチについては最後で

#import "EDViewController.h"
#import "EDTextView.h"
 ・・・
- (void)viewDidLoad
{
[super viewDidLoad];
EDTextView* textview = [[EDTextView alloc]
initWithFrame:CGRectMake(40, 100, 240, 260)];
[self.view addSubview:textview];

}



 Runさせても何も見えませんが、ちゃんと張り付いています。

$テン*シー*シー-7

 で、通常UIViewの内部に文字列を表示させるならUILabelを貼付けて以上。
 なわけですが、目的はCore Textの学習であり縦書きの調査なわけなので、EDTextViewでは-drawRect:メソッドをオーバーライドして、その中でUIGraphicsGetCurrentContext()で戻されるCGContextRefに対して描画をおこないます。
 こういった場合、iOS 6まではUIKitのNSString拡張を利用して

- (void)drawRect:(CGRect)rect
{
// 表示する文字列
NSString* string = @"503 ご~おぉまぁ~り・さん…

// self.bounds全体を青色で塗りつぶす
[[UIColor blueColor] setFill];
UIRectFill(self.bounds);

[string drawInRect:self.bounds withFont:[UIFont systemFontOfSize:14]];
注意: [[UIColor blueColor] setFill];UIRectFill(self.bounds)を実行しておかないと、ビュー全体が黒くなって文字が判別できなくなる。-initFrame:メソッド側でself.backGround = [UIColor blueColor]でも可。
でよかったわけですが、ここらへんは全部、非推奨になってしまったんでNSStringなら-drawInRect:withAttributes:もしくはNSString のかわりにNSAttributedStringを使う事になります。
 もっとも-drawInRect:withAttributes:はiOS 7から利用可能なんで、iOS 6もサポートするんじゃいな人は、NSAttributedString一択っす。

ブランチ:ex1

- (void)drawRect:(CGRect)rect
{
// 表示する文字列
NSString* string = @"503 ご~おぉまぁ~り・さん…

// self.bounds全体を青色で塗りつぶす
[[UIColor blueColor] setFill];
UIRectFill(self.bounds);

// NSAttributedStringを使ったテキスト描画
NSAttributedString *attributedString = [[NSAttributedString alloc] initWithString:string];
[attributedString drawInRect:self.bounds];

}



 Runするとこんな感じ。

$テン*シー*シー-8

 で、NSStringの-drawInRect:withFont:なんかでやっていたフォントの指定はどうやるかというと、NSDictionaryでキーと設定値のペアを作ってNSAttributedStringインスタンス作成時に指定する事になります。
 例えばフォントの指定には NSFontAttributeNameというキーを使う。
 そんでもって、このNSDictionaryを引数に取る-initWithString:attributes:でNSAttributedStringを作成すればOK。

ブランチ:ex2

// 24ポイントフォント使用
UIFont *font = [UIFont systemFontOfSize:24];

// NSAttributedString用の属性を用意
NSDictionary *attributes = @{
NSFontAttributeName:font
};

// 属性指定でNSAttributedStringを作成
NSAttributedString *attributedString = [[NSAttributedString alloc] initWithString:string
attributes:attributes];
[attributedString drawInRect:self.bounds];
モダンObjective-C文法使うと、NSDictionaryやNSArrayの作成が簡単に記述できて、ほんとはかどるわ。モダ…何?な人は日本語ドキュメントページに置いてある「Objective-Cによるプログラミング」ドキュメントを「リテラル構文」で検索すべし。

 Runするとこんな感じ。

$テン*シー*シー-9

 ちなみにフォント以外にもNSForegroundColorAttributeNameキーでテキスト色とか

ブランチ:ex3

// 文字に黄色指定
UIColor* textColor = [UIColor yellowColor];

// NSAttributedString用の属性を用意
NSDictionary *attributes = @{
NSFontAttributeName:font,

NSForegroundColorAttributeName:textColor
};

 NSParagraphStyleAttributeNameキーで段落属性なんてのも指定できる。
 文字の中央よせ配置なんかはこれで指定するわけだ。

ブランチ:ex4

// 段落設定
NSMutableParagraphStyle* paragraphStyle = [[NSMutableParagraphStyle alloc] init];
paragraphStyle.alignment = NSTextAlignmentCenter; // センタリング
paragraphStyle.minimumLineHeight = 40; // 行間40ポイント

// NSAttributedString用の属性を用意
NSDictionary *attributes = @{
NSFontAttributeName:font,
NSForegroundColorAttributeName:textColor,

NSParagraphStyleAttributeName:paragraphStyle
};

 Runするとこんな感じ。

$テン*シー*シー-10

 NSMutableAttributedStringにして、文字列の一部区間にだけ属性を適用させる事も可能。次のように5文字めから10文字分にだけ、これまでの属性を指定すると

NSMutableAttributedString* attributedString = [[NSMutableAttributedString alloc] initWithString:string];
NSRange range;
range.location = 5;
range.length = 10;
[attributedString addAttributes:attributes range:range];

 次のようになります。

$テン*シー*シー-11

 5や10がバイト数の指定ではなく、文字数の指定になってるところも注目ですな。便利。

 んなわけでして…たいがいの事はNSAttributedStringで事足りるわけですよ。
 ただし、さすがに縦書きは無理みたいで、その場合はNSAttributedString の下働きとして使われているCore Textの出番という事になる。

 ということで、まずはCore Textを使うためにプロジェクトにCoreText.frameworkを追加します。ワンツースリー。

$テン*シー*シー-12

 ふぉっぉおおおおおお。
 これでフレームワーク選択画面が出るんで、あとはCoreText.frameworkを選んでAddボタンを押す。

$テン*シー*シー-13

 ここまでできたらEDTextView.mでCoreText/CoreText.hをimportして-drawInRect:部をCore Text版に変更。

ブランチ:ex5

#import
#import "EDTextView.h"
 ・・・
- (void)drawRect:(CGRect)rect
{
NSAttributedString *attributedString = [[NSAttributedString alloc]
initWithString:string
attributes:attributes];

// CTFramesetter用意
CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString(
(__bridge CFAttributedStringRef)(attributedString));

// CTFramesetterを使い、文字列を指定した領域(今回はビューの矩形)に
// 配置させたCTFrameを作成する
UIBezierPath* path = [UIBezierPath bezierPathWithRect:self.bounds];
CTFrameRef frame = CTFramesetterCreateFrame(framesetter,
CFRangeMake(0, 0), path.CGPath, NULL);

// 描画用コンテキスト取り出し
CGContextRef context = UIGraphicsGetCurrentContext();
// 座標系の調整 数学で使う上方向にyが増加する座標系にする
CGContextTranslateCTM(context, 0, self.bounds.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
// テキスト用のアフィン変換用行列を初期化する
CGContextSetTextMatrix(context, CGAffineTransformIdentity);
// 描画
CTFrameDraw(frame, context);

// 作成したCTFramesetterとCTFrameの所有権を放棄する
CFRelease(frame);
CFRelease(framesetter);
}


 Runしても、NSAttributedStringの-drawInRect:と変わりありません。
 ちゅ~か、たぶんNSAttributedStringの-drawInRect:は内部でこういうことやっていると思われ。
 で、これにアレとアレ(ブランチ:ex6)を足すと~

 ハート形に縦書き文字配置~。
 ちゃんと禁則処理もしてくれてます。確認するために指で画面なでるとハートが拡大縮小して文字を配置しなおすようにしてみたお(ブランチ:ex7)。

14
お、おお~、おおぉ~う

 ほらほら、ちゃんと"~"や"」"が先頭に来ないようにとか処理してます。
 出版のプロの目だと話にならんのかもしれんが、素人目には十分ですわ。
 もっとも、私のABプリントは住所が対象なので、英語同様スペース部以外では折り返したくないわけです。ちょっと工夫が必要になるんですが、そこらへんはこれからじっくりやりましょう。

 CTFramesetterとは何なのか、そいつが作り出すCTFrameとは、以下次回!
 待ちきれねーよな人は、ハート縮小拡大まで実装したサンプルプロジェクトをダウンロードして自習だ。

------------
サンプルプロジェクト:coretext.zip

 ちなみに、ブランチ:ex1~ex7の各ステップ間のソース変更の差分はこうやって確認できます。

15

 これで、選んだログ項目(リビジョンという)と、その1つ前のリビジョンの差分を表示する画面が現れる。

16

 確認が終わったら、Donボタンで画面閉じます。
 基本の編集画面に戻るにはShow the standard editorを選択ね。

17

 各ex1~ex7までの段階を試したい場合は、Source Control→coretext master→Switch to Branchメニュー選んでください。
 出てきた画面で、あらかじめ登録しておいたex1~ex7までの好きなブランチ(分岐)地点を選んで、その状態にプロジェクトを変更できます。

18

 Runすると、その地点での処理が試せます。
 最新状態に戻すには、さっきと同じくSource Control→core text {最後に選んだブランチ名}→Switch to Branchメニューを選んで、今度は出てきた画面で、masterを選びます。

19

 これで最新状態に戻る。
 途中で紹介したNSMutableAttributedStringを使う版は無いのであしからず。

 以上ったら以上!