CGRectの convert(_:to:) と convert(_:from:) をやっと理解した
Table of Contents
CGRectの座標系変換はめったにやりませんが、地図を扱うアプリの実装でこれに遭遇したので、理解したことまとめます。 ちなみに今回は「MKMapViewのタップした位置周辺の施設を検索する」というシチュエーションでした。
func convert(_ rect: CGRect,
to view: UIView?) -> CGRect
func convert(_ rect: CGRect,
from view: UIView?) -> CGRect
いずれも、CGRectを読み込ませて、別の座標系に変換したCGRectが返ってくるものです。
登場人物 #
ドキュメントを読んでいると、以下のワードが出てきます。
- a rectangle
- 変換対象となる四角形のこと。boundsだったり、frameだったり。
- receiver
- この関数を呼び出す際の呼び出し元のオブジェクト。
viewA.convert(viewB.frame, from: viewC)
と書く場合は、rectA
がレシーバ。 - another view
- 変換先や変換元の座標系。
convert(_:to:)
ではto
のViewが、convert(_:from:)
ではfrom
のViewが、another viewです。
座標系を変換するには、この3者が必要になるようです。
convert(_:to:) #
例:
viewA.convert(viewB.frame, to: viewC)
この関数では、viewAから見たviewBの位置を、viewCの座標系に変換します。 viewBがviewAのsubViewであるとして、viewBをviewCのsubViewに移動する場合をイメージするとわかるかも……。
convert(_:from:) #
例:
viewA.convert(viewB.frame, from: viewC)
to
と比べて、receiverとanother viewの役割が逆になったものです。
この関数では、viewBの位置を、viewCの座標系からviewAの座標系に変換します。 例えば、viewBがviewCのsubViewだったとして、viewAのsubViewに移動するイメージです。
僕の理解が間違っていなければ、以下と等価ということになりそうです。
viewC.convert(viewB.frame, to: viewA)
実例 #
以下のような画面がありました。
階層構造を一部抜粋します。
- view
- mapView (MKMapView)
- greenBorderView (UIView)
MKMapViewの上に、UIViewが重なっています。
やりたいこと #
greenBorderView
が表示する緑の枠が、mapView
の地図のどの範囲に対応するのかを得たいという状況です。
そもそもgreenBorderView
はmapView
のsubViewではないので、それがmapView
から見てどの位置あるかを知ることはできません。
greenBorderView
の位置をmapView
から見た場合の座標系に変換することで、地図の範囲を得られるようになります。
// viewの座標系にいる greenBorderView の座標を、mapView の座標系に変換する
let convertedRect = view.convert(greenBorderView.frame, to: mapView)
// 地図上の範囲を取得する
let region = mapView.convert(convertedRect, toRegionFrom: mapView)
これで、緑色の枠を地図の範囲に対応させることができました。
convert(_:from:)
とconvert(_:to:)
が、ドキュメントを落ち着いて読むことで、理解できました。