UITextInputえくすとら
いろいろ問題の多いクラスだが、それ以上に組み方がよくわからないクラスのせいだろうか、迷っている人がちらほらいるらしいので、も一度簡単に実装方法をおさらいしておこうかと。時間があれば、サンプルでも作りたいところなのだが。というわけで、とりあえず簡単に手順だけを。
1. UITextPositionを継承したサブクラスを自作
@interface MyTextPosition : UITextPosition <NSCopying> { int position; BOOL beginningOfText; BOOL endOfText; } @property (readonly) int position; @property (readonly) BOOL beginningOfText; @property (readonly) BOOL endOfText; @end
テキストの位置を指定するためのクラス。CGPointのようなノリのものだが、複雑な構造の文書や不可視文字などに対応させるために、クラスになっている。最も単純なプレーンテキストの場合は、単に「何文字目か」のインデックスだけ持っていればよい。ついでに「文章の終端(EOFみたいに)」や「文章の先頭(最初の文字よりも手前)」などの指定ができるようにしておくと、あとあと仕掛けが組みやすい。(ここではBOOLとして端折っているが、メモリコストを考えつつ、よさげな実装でど〜ぞ)
また、後々作業をやりやすくするためにも、NSCopyingのプロトコルを実装しておくことをおすすめする。
2. UITextRangeを継承したサブクラスを自作
@interface MyTextRange : UITextRange <NSCopying> { MyTextPosition *start; MyTextPosition *end; BOOL empty; } @property (readonly, getter=isEmpty) BOOL empty; @property (readonly) UITextPosition *start; @property (readonly) UITextPosition *end; @end
テキストの範囲を決めるクラス。メンバーには、自作のUITextPositionを継承したサブクラスを用意するのだが。このクラスが、意外と注意点が多い。
まず一点目。UITextRangeではUITextPosition形式でのpropertyが要求されているので、外部に紐付けする際はMyTextPositionをダウンキャストしたものを使用する。早い話が、@synthesizeを使用せずに、自力でメソッド作成しろよ、と。どーしても@synthesizeにこだわるのなら、メンバー宣言はUITextPositionとしておいて、実体はMyTextPosition、という手もある。
二点目。リファレンスでの指定はないが、あからさまにNSCopyingプロトコルを実装しておくべきらしいので、こいつを実装。
三点目。この後出てくるUITextInputを利用する側のクラスだが、どうやら所々で「startとendはnilではない」ことを前提として動いている節があったりするんだな、これが。なので、UITextRangeのインスタンスを生成する場合は、必ずstartとendが有る状態にしておいた方がよい。だが、こうしてしまうと、「1文字選択されている」のか「位置だけで選択範囲が0」なのかの区別がつかなくなる。別の場所で管理しても良いのだが、ここにemptyフラグ付けちゃった方が楽かと。
3. UITextInputを継承したビューを作成
@interface MyTextView : UIView <UITextInput, UITextInputTraits> { MyTextRange *selectedTextRange; MyTextRange *markedTextRange; MyTextPosition *beginningOfDocument; MyTextPosition *endOfDocument; id <UITextInputDelegate> inputDelegate; .... (以下略) } @property (readwrite, copy) UITextRange *selectedTextRange; @property (nonatomic, readonly) UITextRange *markedTextRange; .... (以下略) @end
このクラスについては、リファレンスとヘッダーファイル(UITextInput.h)のコメント読みつつ進めていけば、それほど迷わずに済むのではないかと。
ここでも、外部にpropertyしているselectedTextRange等はUITextRange希望、なので、MyTextRangeの時と同様にメソッドを自作するなり(以下略。
あと、キー入力対応させるために、canBecomeFirstResponderを実装することと、タッチ時にカーソル位置を制御したりするためにtouchesBeganを書き書きしたりする必要がある。
あとは、できあがったMyTextViewを、InterfaceBuilderでぺたりと貼り付けるなりすれば、お手軽なボク仕様テキストビュー完成。