すっさんぽ
  1. Posts/

CGRectの convert(_:to:) と convert(_:from:) をやっと理解した

·

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の地図のどの範囲に対応するのかを得たいという状況です。
そもそもgreenBorderViewmapViewの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:)が、ドキュメントを落ち着いて読むことで、理解できました。