🍁
Swift: NSViewのconvertによる座標変換
view
が入れ子になっている時にconvert
を使ってうまい具合に座標変換をしたいと思うのに、convert
の挙動がいまいちわからなかったので検証してみました。
ViewControllerのルートViewの中にViewが二つ入っている状態。
ここで、view.frame
、view1.frame
、view2.frame
をそれぞれ確認するとこんな感じ。
Swift.print("view", view.frame)
Swift.print("view1", view1.frame) // viewから見たview1の座標
Swift.print("view2", view2.frame) // view1から見たview2の座標
frameは親ビューから見た座標を返す。
ではviewから見たview2の座標はどのようにして取得できるのか?
検証してみる
全パターン試してみる。
Swift.print( view.convert(view2.frame, to: nil) ) // view2.frameと変わらない
Swift.print( view.convert(view2.frame, from: nil) ) // view2.frameと変わらない
Swift.print( view.convert(view2.frame, to: view) ) // view2.frameと変わらない
Swift.print( view.convert(view2.frame, from: view) ) // view2.frameと変わらない
Swift.print( view.convert(view2.frame, to: view1) ) // ❌ わけわからん座標1
Swift.print( view.convert(view2.frame, from: view1) ) // ⭕️
Swift.print( view.convert(view2.frame, to: view2) ) // ❌ わけわからん座標2
Swift.print( view.convert(view2.frame, from: view2) ) // ❌ わけわからん座標3
Swift.print( view1.convert(view2.frame, to: nil) ) // ⭕️
Swift.print( view1.convert(view2.frame, from: nil) ) // ❌ わけわからん座標1
Swift.print( view1.convert(view2.frame, to: view) ) // ⭕️
Swift.print( view1.convert(view2.frame, from: view) ) // ❌ わけわからん座標1
Swift.print( view1.convert(view2.frame, to: view1) ) // view2.frameと変わらない
Swift.print( view1.convert(view2.frame, from: view1) ) // view2.frameと変わらない
Swift.print( view1.convert(view2.frame, to: view2) ) // (0.0, 0.0) になった
Swift.print( view1.convert(view2.frame, from: view2) ) // ❌ わけわからん座標4
Swift.print( view2.convert(view2.frame, to: nil) ) // ❌ わけわからん座標3
Swift.print( view2.convert(view2.frame, from: nil) ) // ❌ わけわからん座標2
Swift.print( view2.convert(view2.frame, to: view) ) // ❌ わけわからん座標3
Swift.print( view2.convert(view2.frame, from: view) ) // ❌ わけわからん座標2
Swift.print( view2.convert(view2.frame, to: view1) ) // ❌ わけわからん座標4
Swift.print( view2.convert(view2.frame, from: view1) ) // (0.0, 0.0) になった
Swift.print( view2.convert(view2.frame, to: view2) ) // view2.frameと変わらない
Swift.print( view2.convert(view2.frame, from: view2) ) // view2.frameと変わらない
何パターンかに分かれたので、同じものでまとめてみてみる。
成功パターン(viewから見たview2の座標)
view.convert(view2.frame, from: view1)
view1.convert(view2.frame, to: nil)
view1.convert(view2.frame, to: view)
失敗パターン1(view2.frameと変わらない)
view.convert(view2.frame, to: nil)
view.convert(view2.frame, from: nil)
view.convert(view2.frame, to: view)
view.convert(view2.frame, from: view)
view1.convert(view2.frame, to: view1)
view1.convert(view2.frame, from: view1)
view2.convert(view2.frame, to: view2)
view2.convert(view2.frame, from: view2)
失敗パターン2(原点ゼロになった)
view1.convert(view2.frame, to: view2)
view2.convert(view2.frame, from: view1)
失敗パターン3
view.convert(view2.frame, to: view1)
view1.convert(view2.frame, from: nil)
view1.convert(view2.frame, from: view)
失敗パターン4
view.convert(view2.frame, to: view2)
view2.convert(view2.frame, from: nil)
view2.convert(view2.frame, from: view)
失敗パターン5
view.convert(view2.frame, from: view2)
view2.convert(view2.frame, to: nil)
view2.convert(view2.frame, to: view)
失敗パターン6
view1.convert(view2.frame, from: view2)
view2.convert(view2.frame, to: view1)
検証結果からわかること
-
子ViewA.convert(子ViewB.frame, to: nil)
=子ViewB.convert(子ViewB.frame, to: 親View)
。つまり、convert(_:to:)
では親Viewとnilは同じ扱い。 -
親View.convert(子ViewB.frame, from: 子ViewA)
=子ViewA.convert(子ViewB.frame, to: 親View)
。つまり、from
とto
を入れ替えた時、対応するViewも入れ替えれば同値。
これ、本当かな??
さらに入れ子にした場合で検証する
view2の中にさらにview3を入れて検証しました。
// viewからみたview3の座標
Swift.print( view.convert(view3.frame, from: view2) )
Swift.print( view2.convert(view3.frame, to: nil) )
Swift.print( view2.convert(view3.frame, to: view) )
// view1から見たview3の座標
Swift.print( view1.convert(view3.frame, from: view2) )
Swift.print( view2.convert(view3.frame, to: view1) )
という感じになった。
上で述べた「convert(_:to:)
では親Viewとnilは同じ扱い。」というのは捉え方によっては間違い。あるViewにとって直属の親のViewを親Viewと捉えた場合は法則に沿わない。あくまでルートViewの時の動作。実際に挙動を確認してみても、欲しい座標への変換を自在に行うのは難しそうに感じる。実験してみて確信を持たないと動きが読みづらい。
Discussion