Objective-C
iOS
座標系

[Objective-C] UIViewの座標と座標変換について

More than 3 years have passed since last update.

UIViewはそれぞれ座標系を保持しています。

自身のView座標系の値はview.bounds、親ビューからの相対位置はview.frameが保持しています。

言葉だと分かりづらいので、図にすると以下のような値をそれぞれ保持していることになります。

(cview1、cview2ともにUIView)

cap.png

実際に上記の状態で以下のログを出力してみます。

//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:toViewconvertPoint: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.cview1self.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にすると違った結果になってしまうので注意が必要です。