UIViewはそれぞれ座標系を保持しています。
自身のView座標系の値はview.bounds
、親ビューからの相対位置はview.frame
が保持しています。
言葉だと分かりづらいので、図にすると以下のような値をそれぞれ保持していることになります。
(cview1、cview2ともにUIView)
実際に上記の状態で以下のログを出力してみます。
//cview1のframeとboundsを出力
NSLog(@"cview1.frame : %@", NSStringFromCGRect(self.cview1.frame));
NSLog(@"cview1.bounds: %@", NSStringFromCGRect(self.cview1.bounds));
//cview2のframeとboundsを出力
NSLog(@"cview2.frame : %@", NSStringFromCGRect(self.cview2.frame));
NSLog(@"cview2.bounds: %@", NSStringFromCGRect(self.cview2.bounds));
cview1.frame : {{0, 0}, {320, 240}}
cview1.bounds: {{0, 0}, {320, 240}}
cview2.frame : {{0, 240}, {320, 240}}
cview2.bounds: {{0, 0}, {320, 240}}
ログを見ると一目瞭然ですね。
frame.origin
は親ビューからの相対位置が保持されているのに対し、bounds.origin
は自身の座標系になっているのが分かります。
以上を踏まえた上で。
とある位置(例えばタッチされた位置など)が与えられたとして、それぞれの座標系ではどの位置なのかというのを取得したい場合があります。
そのときに使うのが、UIViewのインスタンスメソッドconvertPoint:toView
やconvertPoint:fromView
というメソッドになります。
これはそれぞれレシーバ(※)から対象(あるいはその逆)の座標変換を行うメソッドです。
簡単なテストをしてみましょう。以下のように指定し、ログに出力してみます。
※…上記メソッドを実行しているインスタンス
##convertPoint:fromView:
サンプル
//touchオブジェクトを取得
UITouch *touch = [touches anyObject];
//タッチ位置が、該当のView上のどこかを取得
CGPoint touchpoint1 = [touch locationInView:self.cview1];
//cview1からcview2の座標系に位置を変換
CGPoint touchpoint2 = [self.cview2 convertPoint:touchpoint1 fromView:self.cview1];
NSLog(@"touchpoint1: %@", NSStringFromCGPoint(touchpoint1));
NSLog(@"touchpoint2: %@", NSStringFromCGPoint(touchpoint2));
touchpoint1: {93.5, 184.0}
touchpoint2: {93.5, -56.0}
サンプルでは、touchesBegan:withEvent:
メソッドで得られたタッチ位置を元に、self.cview1
とself.cview2
のそれぞれの座標系での位置を出力しています。
3D系に詳しい人であれば、 ワールド座標系とモデル座標系の座標変換をイメージ すると分かりやすいと思います。
##convert***系メソッド
上記のメソッドにはいくつか類似のメソッドがあります。
メソッド | 型 | 説明 |
---|---|---|
convertRect:toView: | CGRect | レシーバの座標系をtoView 座標系に変換 |
convertRect:fromView: | CGRect |
fromView 座標系をレシーバの座標系に変換 |
convertPoint:toView: | CGPoint | レシーバの座標系をtoView 座標系に変換 |
convertPoint:fromView: | CGPoint |
fromView 座標系をレシーバの座標系に変換 |
上記メソッド、なにをどう変換するかをしっかり把握しておくことが大事です。
// aView座標系の座標`aView.bounds`を、`toView`座標系に変換
[aView convertRect:aView.bounds toView:otherView];
上記ポイントは、aView.bounds
を変換している点です。これをaView.frame
にすると違った結果になってしまうので注意が必要です。