UILabelみたいなものを作ってみる

文字列の描画練習でUILabelみたいなものを作ってみました.Viewの文字列や画像などの描画サンプルになります.実際のコードではUILabelをSubViewに追加する方法が最も手間がかからずいい方法だと思います.

追記(2010年1月18日)

描画については MonoTouch Sample: Core Graphics and UIImageView | Sabon Rai Software が参考になる。

手順

  • テキストを描画するためのUIViewを継承するクラスを作る (CustomLabelViewクラス)
  • UIApplicationDelegateUIViewのFinishedLaunching()メソッドなどで,上記のインスタンスオブジェクトをUIWindowのsubViewに追加する.

コード

まず CustomLabelView.cs ファイルにCustomLabelViewクラスを書いていきます.
コードは下記のとおり:

using System;
using System.Drawing;
using MonoTouch.UIKit;
using MonoTouch.CoreGraphics;

namespace Sample_DrawString
{
	public class CustomLabelView : UIView
	{
		String _text;
		public string Text
		{
			get{return _text;} 
			set{ _text = "(custom) " + value;}
		}
		
		public CustomLabelView (RectangleF frame) : base(frame)
		{
			initialize();
		}
		void initialize()
		{
			_text = String.Empty;
		}
		
		public override void Draw (RectangleF rect)
		{
			var gc = UIGraphics.GetCurrentContext();
			gc.ScaleCTM(1, -1);
			gc.SetTextDrawingMode(CGTextDrawingMode.Fill);
			gc.SelectFont("Helvetica-Bold", 22, CGTextEncoding.MacRoman);
			gc.SetRGBFillColor(0,0,0,1);
			gc.ShowTextAtPoint(40, -30, _text, _text.Length);
		}
	}
}

これは,設定したテキストの先頭に"(Custom) "という接頭語をつけたテキストを画面に表示します.
ポイントは,1. Draw() メソッドで描画する, 2. CGContextのScaleCTM()メソッドでY軸を反転した座標系に変換する,です.
まずViewの独自の描画はDraw()メソッドをオーバライドして実装します.iPhoneプログラミングガイドにあるように,iPhone OSは描画が必要になったときだけDraw()メソッドを呼び出します.Draw()メソッドの描画結果はiPhone OSがビットマップとして保持して再利用します.これにより描画処理負荷が軽減されます.
Draw()メソッドが呼ばれる前に,そのViewに適したCGContextが設定されます.それをUIGraphics.GetCurrentContext()メソッドで取得します.

次に文字列を描画します.Objective-Cであれば,Stringクラスの拡張メソッドなどで簡単に描画できるのですが,(自分でクラス拡張すればいいのですが)そんなものはMonotouchにはないので,CGContextを使い描画します.やっていることは,文字の色とフォントを設定して指定した位置に文字列を描画しているだけです.

ここで注意するのはViewとCGContextの座標系の違いです.Viewの座標系は,左上に原点があり,左から右方向にx軸,上から下方向にy軸を取ります.一方でCGContextは第1象限の座標を想定していますから,このまま描画すると(Viewの座標系はy軸が反転しているため)文字列が上下逆さまになります.このためにScaleCTM()メソッドを呼び出して変換行列をy軸が反転するように設定しています.

これを図にすると左図がViewの座標系です.ScaleCTM()メソッドでy軸を反転した座標系がCGContextの座標系が右図になります.原点はいじっていませんから,文字の描画位置のy座標には,負の値を設定することになります.

今回の失敗

UIViewクラスにあるDrawString()メソッドでテキスト描画をしようとしましたが,例外が発生しました.スタックトレースには "DrawStringというセレクタが見つからない" と表示されていました.DrawStringが実装されていないと知らずにUIViewクラスにメソッドがあるのだからと呼び出したために,このエラーを出してしまったわけです.
これはMonotouchとObjective-Cのつながりで気をつける部分だなと思いました.Objective-Cセレクタという仕組みは実行時にメソッドを動的に解決するために,実装されていなくてもよいのです.一方で静的な型のC#は宣言したメソッドは(NotImplementedExceptionを投げるコードが書かれているかもしれませんが)実装されていなければ,コンパイル時エラーではじかれます.
UIViewのセレクタに対応したメソッドがあるからといって,それが実装されているとは限らないと知らねばならないわけですね.

まとめ

UILabelみたいなViewを作ってました.描画ではViewとCGContextの座標系の違いに注意しなければなりません.具体的にはCGContextのScaleCTM()メソッドでY軸を反転させて,Y軸の座標設定に注意しつつ作業する必要があります.(やっていることはアフィン変換ですから,ViewのFrameのHeightだけy軸を下にしてやれば,何も考える必要もなくなりますけど)