search
LoginSignup
6
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

posted at

updated at

Organization

今更だけどUIViewの座標変換系メソッドを理解する(Swift)

Xcode-12 iOS-14.0 Swift-5.3

はじめに

ごくまれに UIView の convert 系のメソッドを使って座標を変換したいときがありますがいつもどっちに何指定するのかな?と忘れるのでまとめておきます。

View 準備

下記のような構成で座標変換をしてみます。

xib

views

ScrollView を ViewController の View と同じサイズで配置してその中に contentView (緑色)を幅は ScrollView と同じ高さは 1200 pt で配置しています(StackView は左端のメモリ用です)。contentView の中に縦横 120 pt の targetView (白色)を contentView の Top 900pt、横は中央寄せで配置して、その targetView (白色)の中心にボタンを配置しています。

座標変換

座標変換には UIView に下記のメソッドが用意されており、これを使って変換します。

どちらのメソッドも対象の View は同一 Window にある必要があります。引数の viewnil の場合は Window の座標系に変換されます。

convert(_: to:)

point はレシーバーの座標系を指定し、view には変換したい座標系の View を指定します。

func convert(_ point: CGPoint, to view: UIView?) -> CGPoint

// ex.
aView.convert(CGPoint.zero, to: bView)

例だと aView の左上の座標を bView の座標系に変換しています。

convert(_: from:)

pointview の座標系を指定し、レシーバーの座標系に変換します。

func convert(_ point: CGPoint, from view: UIView?) -> CGPoint

// ex.
aView.convert(CGPoint.zero, from: bView)

例だと bView の左上の座標を aView の座標系に変換しています。

実践

ボタン押下時に targetView の座標系から scrollView と ViewController の view の座標系に変換してみます!

 @IBAction private func convertPoints(_ sender: Any) {
    // targetViewの左上の座標をscrollViewの座標系に変換
    let p1 = targetView.convert(CGPoint.zero, to: scrollView)
    print("p1: \(p1)") // p1: (127.5, 900.0)

    // targetViewの左上の座標をviewの座標系に変換
    let p2 = targetView.convert(CGPoint.zero, to: view)
    print("p2: \(p2)") // p2: (127.5, 367.0) * scrollViewのoffsetで変わる

    // targetViewの左上の座標をscrollViewの座標系に変換 
    let p3 = scrollView.convert(CGPoint.zero, from: targetView)
    print("p3: \(p3)") // p3: (127.5, 900.0)

    // targetViewの左上の座標をviewの座標系に変換 
    let p4 = view.convert(CGPoint.zero, from: targetView)
    print("p4: \(p4)") // p4: (127.5, 367.0) * scrollViewのoffsetで変わる
}

上記を見てもらうと分かる通り、p1 と p3、p2 と p4 は同じ結果になります。

これを使ってボタン押下時に targetView が画面外にある場合にスクロールのオフセットを調節してみます。

@IBAction private func convertPoints(_ sender: Any) {
    // targetViewの左下の座標をviewの座標系に変換
    let p1 = targetView.convert(CGPoint(x: 0, y: targetView.frame.height), to: view)
    // targetViewの左下がviewの範囲内か確認
    if !view.frame.contains(p1) {
        // targetViewが画面内におさまるようスクロール
        let p2 = targetView.convert(CGPoint(x: 0, y: targetView.frame.height), to: scrollView)
        scrollView.setContentOffset(.init(x: 0, y: p2.y - scrollView.frame.height), animated: true)
    }
}

こんな感じです:tada:

scroll

おわりに

to と from の意味を考えたらわかるんですが point はどの座標系のやついれればいいんだっけ?とよく迷います。。。

もうこれで迷わない!!(たぶん:rolling_eyes:

ちなみに Rect の変換メソッドもあります。

SwiftUI になるとこういうの使うこともなくなるのかな:thinking:

参考

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
6
Help us understand the problem. What are the problem?