Text Kitによるテキストレイアウト(前編)(2ページ目)
これらのクラス関係の例を下図に示します(図16)。この例は、テキストストレージが持つテキストコンテンツを、2ページ(もしくは2カラム)に分割して表示する場合のクラス関連図です。そのため、レイアウトマネージャが2つ存在しています。
図16: Text Kitのクラス関係(複数ページに分割表示する例)
UITextViewのインスタンスを生成すると、そのUITextViewに関連付けされたNSTextContainer、NSLayoutManager、NSTextStorageのインスタンスが生成され、プロパティを通してアクセス可能となります。ただし、これらのプロパティはreadonlyであるため、生成後に変更することはできません。
テキストビューに自前のテキストコンテナを対応付けるには、以下のメソッドを利用してコードからUITextViewのインスタンスを生成します。
[ソースコード39] 自前のテキストコンテナを持つUITextViewを生成するメソッド
- (instancetype)initWithFrame:(CGRect)frame
textContainer:(NSTextContainer *)textContainer;
レイアウトマネージャを利用すると、位置座標から表示されている文字を取得することが可能です。これをヒットテストと呼びます。例えば、タップ位置(タッチ終了位置)に表示されている文字は、下記のコードで取得できます。
[ソースコード40] 座標から文字インデックスを取得(ヒットテスト)
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint locationInView = [touch locationInView:self.textView];
NSTextContainer *textContainer = self.textView.textContainer;
NSLayoutManager *layoutManager = self.textView.layoutManager;
// テキストコンテナの座標系に変換
CGPoint locationInContainer =
CGPointMake(locationInView.x - self.textView.textContainerInset.left,
locationInView.y - self.textView.textContainerInset.top);
// 文字インデックスを取得
NSUInteger charIndex = [layoutManager characterIndexForPoint:locationInContainer
inTextContainer:textContainer
fractionOfDistanceBetweenInsertionPoints:NULL];
// テキストストレージから該当文字を取得
NSString *charString =
[[self.textView.textStorage string] substringWithRange:NSMakeRange(charIndex, 1)];
}
上記の処理内容はシンプルです。まずtextViewの座標系におけるタッチ位置を取得します。得られた位置をテキストコンテナの座標系に変換します。このとき、UITextViewが持つtextContainerInsetプロパティの値を用います。テキストコンテナの座標系でのタッチ位置が得られたら、あとはそこから文字インデックスを取得できます。