目的
UIViewが表示する画像は、実際はプロパティlayerが管理している事を理解する。
主要クラス
UIView,CALayer
使用テンプレートプロジェクト
Window-based Application
プロジェクトの名称
Draw
事前に体験しておくべきドリル
サンプル実装説明
このドリルでは「カスタムUIViewの内容部を独自に描画する」で作成したプロジェクトを拡張する。
元々のDrawView.mのinitWithFrame:メソッドに以下を追加する。このメソッドを扱うために
QuartzCore/QuartzCore.h
が必要。
#import <QuartzCore/QuartzCore.h>
・
・
- (id)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
self.layer.contentsRect = CGRectMake(0, 0, 0.5, 0.5);
}
return self;
}
・
・
- (id)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
self.layer.contentsRect = CGRectMake(0, 0, 0.5, 0.5);
}
return self;
}
実行すると、以下のような表示となる。
オリジナルの状態と見比べると、左上1/4の領域が表示されている。
いったい
self.layer
とは、なんなのか?
答えを先に言えば、UIViewが、必ず一つ持つiPhone画面に表示するオフスクリーン画像管理クラスであるCALayerクラスまたはその継承クラスのインスタンスということになる。
実は、UIViewのdrawRect:メソッドはiPhone画面上に描画するのではなく、このself.layerが管理しているオフスクリーンへの描画だった。
そして
self.layer.contentsRect = CGRectMake(0, 0, 0.5, 0.5);
とは、そのオフスクリーン画像のどの領域を抜き出して表示するかという指定になる。
contentsRectに指定するオフスクリーンの矩形は縦長、横長にかかわらず0~1.0の範囲に正規化したものを使う。
そのため
0, 0, 0.5, 0.5
は画像の左上1/4の矩形を表示する指定になる。
self.layer.contentsRect = CGRectMake(0.3, 0.5, 0.5, 0.5);
など、いろいろ値を変えて結果を確認してみて欲しい。
次に、self.layerがdrawRect:メソッドの描画対象になっている事を確認するために以下のdrawLayer:inContext:メソッドを実装する。
- (void)drawRect:(CGRect)rect {
printf("current context %p\n", UIGraphicsGetCurrentContext());
[[UIColor blueColor] setFill];
UIRectFill(CGRectInset(self.bounds, 10, 50));
}
-(void)drawLayer:(CALayer*)layer inContext:(CGContextRef)context
{
printf("arg layer = %p, layer = %p and context %p\n", layer, self.layer, context);
[super drawLayer:layer inContext:context];
}
printf("current context %p\n", UIGraphicsGetCurrentContext());
[[UIColor blueColor] setFill];
UIRectFill(CGRectInset(self.bounds, 10, 50));
}
-(void)drawLayer:(CALayer*)layer inContext:(CGContextRef)context
{
printf("arg layer = %p, layer = %p and context %p\n", layer, self.layer, context);
[super drawLayer:layer inContext:context];
}
実行するとコンソール(コンソールの表示はコンソールを表示するを参照)には同じアドレスが表示されている事がわかる。
arg layer = 0x6927d30, layer = 0x6927d30 and context 0x6929370
current context 0x6929370
current context 0x6929370
UIViewにこのメソッドが実装されている事は、UIView Referenceのlayer プロパティの説明からわかる。UIViewは自身のlayerのデリゲートとしても機能している。
ちなみにUIViewインスタンスを、他のCALayerのデリゲート(モーダルビューを表示する(2)を参照)として渡す事が禁じられている点は非常に重要。Technical Q&A QA1637も興味深い。
プロジェクト
検討
UIResponderを継承せず、タッチイベントをレポートしない事を考えると、CALayerインスタンスはUIViewの画面描画担当インスタンスと考えることが出来る。
また、UIViewは、必ず一つCALayerを持つが、CALayer自体はUIView同様、入れ子状態にできる。
うまくCALayerを入れ子状にして管理すれば、効率的な表示やアニメーションが実現できることになる。
次回のドリルでは、この入れ子になったCALayerを使ったアニメーションを学習する。