これらのクラス関係の例を下図に示します(図16)。この例は、テキストストレージが持つテキストコンテンツを、2ページ(もしくは2カラム)に分割して表示する場合のクラス関連図です。そのため、レイアウトマネージャが2つ存在しています。
図16: Text Kitのクラス関係(複数ページに分割表示する例)
UITextViewのインスタンスを生成すると、そのUITextViewに関連付けされたNSTextContainer、NSLayoutManager、NSTextStorageのインスタンスが生成され、プロパティを通してアクセス可能となります。ただし、これらのプロパティはreadonlyであるため、生成後に変更することはできません。
テキストビューに自前のテキストコンテナを対応付けるには、以下のメソッドを利用してコードからUITextViewのインスタンスを生成します。
[ソースコード39] 自前のテキストコンテナを持つUITextViewを生成するメソッド
1 | - (instancetype)initWithFrame:(CGRect)frame |
2 | textContainer:(NSTextContainer *)textContainer; |
レイアウトマネージャを利用すると、位置座標から表示されている文字を取得することが可能です。これをヒットテストと呼びます。例えば、タップ位置(タッチ終了位置)に表示されている文字は、下記のコードで取得できます。
[ソースコード40] 座標から文字インデックスを取得(ヒットテスト)
01 | - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event |
03 | UITouch *touch = [touches anyObject]; |
04 | CGPoint locationInView = [touch locationInView:self.textView]; |
06 | NSTextContainer *textContainer = self.textView.textContainer; |
07 | NSLayoutManager *layoutManager = self.textView.layoutManager; |
09 | CGPoint locationInContainer = |
10 | CGPointMake(locationInView.x - self.textView.textContainerInset.left, |
11 | locationInView.y - self.textView.textContainerInset.top); |
13 | NSUInteger charIndex = [layoutManager characterIndexForPoint:locationInContainer |
14 | inTextContainer:textContainer |
15 | fractionOfDistanceBetweenInsertionPoints:NULL]; |
18 | NSString *charString = |
21 | CGRect imageRect = CGRectMake(100, 100, |
22 | imageView.image.size.width, |
23 | imageView.image.size.height); |
24 | imageView.frame = imageRect; |
25 | [self.textView addSubview:imageView]; |
27 | // 非表示領域の計算(画像の表示位置をテキストコンテナの座標に変換) |
28 | CGRect exclusionRect = CGRectOffset(imageView.frame, |
29 | -self.textView.textContainerInset.left, |
30 | -self.textView.textContainerInset.top); |
32 | UIBezierPath *path = [UIBezierPath bezierPathWithRect:exclusionRect]; |
33 | self.textView.textContainer.exclusionPaths = @[path]; |
上記の通り、非表示領域を設定してその位置に画像を表示すれば、テキストの回り込みが実現できます。exclusionRectとimageRectは異なることに注意してください。exclusionRectはNSTextContainerの座標系、imageRectはUITextViewの座標系で示されます。
NSTextContainerによる表示領域は、デフォルトでsizeプロパティにより示されるサイズを持った矩形領域となっています。これに対して、より複雑な領域を表示領域として設定するには、NSTextContainerをサブクラス化して以下のメソッドをオーバーライドします。
[ソースコード42] ライン矩形を示すメソッド
1 | - (CGRect)lineFragmentRectForProposedRect:(CGRect)proposedRect |
2 | atIndex:(NSUInteger)characterIndex |
3 | writingDirection:(NSWritingDirection)baseWritingDirection |
4 | remainingRect:(CGRect *)remainingRect; |
テキストコンテナが示す表示領域の概要を示します(図17)。
lineFragmentRectForProposedRect...メソッドに渡されるproposedRectは、レイアウトマネージャーが計算した行の表示領域で、実際にテキストを表示すべき領域を返します。また、exclusionPathsによる非表示領域があると、非表示領域で分割された領域がremainingRectとして渡されます。そのため、テキストの分割位置もこのメソッドで調整します。
なお、指定したlineFragmentRectに対して、実際にテキストが表示される領域は、lineFragmentPaddingを除いた内側になります。lineFragmentPaddingはNSTextContainerのプロパティで、デフォルト値として0.5が設定されています。
図17: 表示領域の詳細設定に関するパラメータ
この記事のもとになった書籍 |
西方 夏子 著/丸山 弘詩 編
価格:3,800円+税
発売日:2014年06月13日発売
ISBN:978-4-8443-3608-2
発行:インプレスジャパン
|
UIKit徹底解説 iOSユーザーインターフェイスの開発
ユーザーインターフェイス(UI)を制御するUIKitは、iOSアプリの中心的役割を担います。UIKitを深く理解することが、アプリ開発での多くの問題解決に繋がります。本書では、アプリの起動から画面表示、画面レイアウト、イベントハンドリングと、常に稼働しているUIKitの機能を丁寧に解説します。メインとなるコンテンツの表示からステータスバーを含むバーの制御、フォントやテキストの改良など、ビューやテキストの「外観」(アピアランス)を改善する内容も充実させています。使用頻度の高いテーブルビューとコレクションビュー、そしてユニバーサルアプリ化に有益なカスタムコンテナビューコントローラに関しては、チュートリアル形式で解説します。また、StoryboardやAuto Layoutも今後のアプリ開発でさらに重要となる状況を見越して、詳細解説に留まらず同じくチュートリアルで多くの具体例を紹介します。
 
|