UITextViewに罫線を表示してみる

標準アプリでいうとメモ帳アプリがそんな感じですが、編集可能なUITextViewに罫線を表示したかったのです。
で色々ググってみた。例えばこんなコードが出てきましたよ。

http://www.questionhub.com/StackOverflow/3492045

このコードはUITextViewのdrawRect:で各行に線を描いて、スクロールのたびに再描画をかけてますね。線を描くだけなら速度の問題は無いのかもしれませんが…ちょっとそれはイヤンな感じがしました。

で、自分で作ることにしました。足りない頭で考えた方法はこうです。

  • 行の高さを取得する。上記のコードではわざわざsizeWithFont:を使ってますが、UITextViewにlineHeightプロパティがあるのでこれを使います。
  • 一個のUIViewサブクラスを用意して、それにUITextViewが表示している範囲の罫線を描画する*1
  • あとはUITextViewのサブビューにしてやって、スクロールに応じて位置を調整する。

UITextViewのサブビューにしてやれば自動でスクロールには付いてきますけど、長いテキストの場合表示されてない部分の罫線まで用意してやる必要は無いですよね。ある程度スクロールされたら一行分ずらせばいいって寸法です。
とりあえず動けばいいや的なコードはこんなん。

- (void)drawRect:(CGRect)rect {
	CGContextRef context = UIGraphicsGetCurrentContext();
	NSUInteger height = lineHeight; 
	NSUInteger contentHeight = rect.size.height; 
	
	NSUInteger offset = height;
	contentHeight += offset;
	CGContextSetRGBStrokeColor(context, 0.8, 0.8, 0.8, 1);
	CGContextSetLineWidth(context, 0.5);
	for(int i=offset;i < contentHeight;i+=height) {
		CGPoint lpoints[2] = { CGPointMake(0, (float)i+0.5), CGPointMake(rect.size.width, (float)i+0.5) };
		CGContextStrokeLineSegments(context, lpoints, 1);
	}
}

- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
	RTDRuledTextView *ruledTextView = (RTDRuledTextView *)scrollView;
	
	CGPoint contentOffset = scrollView.contentOffset;
	CGFloat newY = ((int)(contentOffset.y)/lineHeight) * lineHeight + lineOffset;
	
	CGRect newFrame = self.frame;
	if (newFrame.origin.y == newY || newY < 0) return;
	newFrame.origin.y = newY;
	self.frame = newFrame;
}

UIViewサブクラスで、UITextViewサブビューにされます。UITextViewサブクラスのほうから行の高さとかオフセットを設定されたり、delegateにされたりしています。

完成品はこんなん。タイトルの部分は別のUITextFieldですけど。

問題点としては、UITextViewに英語フォントが設定されてる状態で日本語を入力したりすると、設定されているフォントと実際に表示に使われているフォントが変わってしまい、lineHeightが目的の値を返してくれません。なので日本語フォントを設定しておきましたが…これで各国語を入力したらどうなるのか、危ういです。
あとまだWaiting For Reviewです。

*1:CALayerで十分な気はする