Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

今更だけど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 の変換メソッドもあります。
* convert(_: to:)
* convert(_: from:)

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

参考

engineerlife
技術力をベースに人生を謳歌する人たちのコミュニティです。
https://community.camp-fire.jp/projects/view/280040
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away