水曜日, 9月 14, 2011

Cocoaでの上下逆さま

Cocoaでプログラミングを始めて惑ったことのひとつに
座標系(Cordinate system)がWindowsなどと違うことだ。
多くの言語では画面上の座標の原点は左上隅にあり、
垂直方向の座標は下に行くほど大きくなる。
Cocoaでは原点は左下にあり、垂直方向に上にいくほど座標が
大きくなる。(これは数学や、日常的な座標系とおなじである。)
とはいえ、このこと自体は慣れると思う。

ただ、左下隅が原点であるということによる不都合もある。
スクロールビュー(NSScrollView)の中に表示するビュー(NSViewなど)
がデフォルトでは左下隅にくっついてしまうのだ。

外側の青い部分がスクロールビュー
内側の白い部分が内容を表示するビュー






この状態では、アプリケーションの使い勝手が悪くなってしまう。
これをこういう状態になおしたい。


この場合、内側のビュー(NSViewなどを継承したビュー)
のクラスにて isFlipped をオーバーライドし、戻り値としてYESを返すようにするだけでよい。
- (void)isFlipped{
    return YES;
}




ビューに直接描画しているのであれば、これでOKなのであるが、
いったんレイヤー(CGLayer)に描画してからビューに投影している
ような場合、不都合が残っている。


左図のように鏡で上下が逆さまになった状態となってしまう。
レイヤーはFlip(上下反転)していないままそこに Hello と描画している。そのレイヤーをFlipされたビューに描画する際にひっくりかえるのだ。


よって、レイヤーもFlipしておく必要がある。
レイヤーに描画する前にそのコンテキストに対して以下を施す。



CGContextTranslateCTM(layerContext, 0, layerHeight );

CGContextScaleCTM(layerContext, 1, -1);


一つ目で、原点を垂直方向に移動させる座標変換がコンテキストに登録され
二つ目で、垂直方向の座標の符号を逆転する座標変換が登録される。
例えば横幅100 x 縦100 のサイズを持つレイヤー上の
座標(10, 20) はFlip(上下反転)すると
一つ目の変換では 座標(0, 100) を原点とした場合の座標に変換されれるので
(10, -80) となる。
二つ目の変換では垂直方向の符号を逆転するので、(10, 80) となる。

※つまり下から20ポイントの場所は高さ100ポイントの場所からみると
下に向かって80ポイントの場所にあたるということである。

このようにしてFlipされたレイヤーには上下反転して描画される。
そのレイヤーをFlipされたViewに描画すると、さらに上下反転され
正常な状態で表示される。

0 件のコメント: