UIViewクラスには2つのビュー間のCGPointやCGRectを取得メソッドが用意されています。
- (CGPoint)convertPoint:(CGPoint)point toView:(UIView *)view;
- (CGPoint)convertPoint:(CGPoint)point fromView:(UIView *)view;
- (CGRect)convertRect:(CGRect)rect toView:(UIView *)view;
- (CGRect)convertRect:(CGRect)rect fromView:(UIView *)view;
使用例
CGPoint point = [viewA convertPoint:CGPointZero toView:viewB];
上記を簡単に説明すると、pointの値はviewAの原点(CGPointZero)の位置が、viewBの原点から見てどの位置になるかです。
※ ここでの原点は bounds.origin.x == 0, bounds.origin.y == 0
を指しています
convert系の4つのメソッドはとても優秀で、
- 異なるsubviews間
- スクロールビューのcontentOffset
- 回転
も全て考慮されて正確に取得することが出来ます。
しかし、通常は考慮されるはずの回転が考慮されない場合があります。
それは対象のビューの回転処理が管理されていない(回転処理が行われていない)場合です。
例えば、
- ビュー階層から外れているビュー
- keyWindow
などがそうです。
ビューの回転はboundsとtransformで実現していますので、そういった管理されていないビューは @property(nonatomic) CGAffineTransform transform
の値が回転に合わせて変更されていないことから確認出来ます。
そして、おそらくconvert系のメソッドは transform
の値を考慮して回転の計算が行なわれているため、回転処理が行われていないビューに対しては正常に取得出来ないと考えられます。
今回は、そういった回転処理が管理されていないビューに対して正しい値を取得したかったので実装してみました。
実装
windowの原点から見たviewAの原点の位置を求めます。
単純に回転に合わせて計算します。
CGPoint origin = [self.viewA convertPoint:CGPointZero toView:self.view.window];
UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
switch (orientation) {
case UIInterfaceOrientationPortrait:
break;
case UIInterfaceOrientationPortraitUpsideDown:
origin.x -= self.viewA.bounds.size.width;
origin.y -= self.viewA.bounds.size.height;
break;
case UIInterfaceOrientationLandscapeLeft:
origin.y -= self.viewA.bounds.size.height;
break;
case UIInterfaceOrientationLandscapeRight:
origin.x -= self.viewA.bounds.size.width;
break;
default:
break;
}
※ - addSubview:
元のビューによってはステータスバーの高さも計算に入れる必要がある場合があります。
限定的なケースですが、参考になれば幸いです。
※ こんな計算しなくても取得出来るメソッドがあるかなと思って探してみたのですが、見つかりませんでした。