convertRect:toView:、convertRect:FromView:、convertPoint:toView :、およびconvertPoint:fromView:メソッドを理解する


128

これらのメソッドの機能を理解しようとしています。それらのセマンティクスを理解するための簡単なユースケースを提供していただけませんか?

ドキュメントから、たとえば、convertPoint:fromView:メソッドは次のように説明されています。

指定されたビューの座標系からレシーバーの座標系にポイントを変換します。

何をしないシステムの座標の平均を?およそ何レシーバ

たとえば、次のようにconvertPoint:fromView:を使用しても意味がありますか?

CGPoint p = [view1 convertPoint:view1.center fromView:view1];

NSLogユーティリティを使用して、p値がview1の中心と一致することを確認しました。

前もって感謝します。

編集:興味のある方のために、これらのメソッドを理解するための簡単なコードスニペットを作成しました。

UIView* view1 = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 150, 200)];
view1.backgroundColor = [UIColor redColor];

NSLog(@"view1 frame: %@", NSStringFromCGRect(view1.frame));        
NSLog(@"view1 center: %@", NSStringFromCGPoint(view1.center));   

CGPoint originInWindowCoordinates = [self.window convertPoint:view1.bounds.origin fromView:view1];        
NSLog(@"convertPoint:fromView: %@", NSStringFromCGPoint(originInWindowCoordinates));

CGPoint originInView1Coordinates = [self.window convertPoint:view1.frame.origin toView:view1];        
NSLog(@"convertPoint:toView: %@", NSStringFromCGPoint(originInView1Coordinates));

どちらの場合も、self.windowがレシーバーです。しかし、違いがあります。最初のケースでは、convertPointパラメータはview1座標で表されます。出力は次のとおりです。

convertPoint:fromView:{100、100}

2番目のものでは、代わりに、convertPointはスーパービュー(self.window)座標で表されます。出力は次のとおりです。

convertPoint:toView:{0、0}

回答:


184

各ビューには独自の座標系があります。原点は0、0、幅と高さです。これはbounds、ビューの長方形で説明されています。frameビューのは、しかし、そのスーパーの境界矩形内の点においてその起源を有することになります。

ビュー階層の最も外側のビューの起点は0,0で、iOSの画面の左上に対応しています。

このビューに20,30のサブビューを追加すると、サブビューの0,0のポイントはスーパービューの20,30のポイントに対応します。この変換は、これらのメソッドが行っていることです。

上記の例は、ポイントをビューからそれ自体に変換するため、何も起こりません。より一般的には、ビューのあるポイントがそのスーパービューとの関係でどこにあるかを見つけます。たとえば、ビューが画面から移動しているかどうかをテストするには、次のようにします。

CGPoint originInSuperview = [superview convertPoint:CGPointZero fromView:subview];

「レシーバー」は、メッセージを受信して​​いるオブジェクトの標準のObjective-C用語です(メソッドはメッセージとも呼ばれます)。したがって、この例では、レシーバーはsuperviewです。


3
jrturton、返信ありがとうございます。非常に役立つ説明。convertPointおよびconvertRect戻り値の型が異なります。CGPointまたはCGRect。しかし、何についてfromto?使用できる経験則はありますか?ありがとうございました。
ロレンソB

変換したいときから、変換したいときまで?
jrturton 2011

3
スーパービューがサブビューの直接の親ビューでない場合でも、機能しますか?
Van Du Tran

3
@VanDuTranはい、同じウィンドウ(iOSアプリのほとんどのビューと同じウィンドウ)にある
限り

すばらしい答えです。私のために多くを片付けるのを助けました。追加の読書?
JaeGeeTee 2014年

39

私はいつもこれがわかりにくいので、convert関数の機能を視覚的に探索できる遊び場を作りました。これはSwift 3とXcode 8.1bで行われます:

import UIKit
import PlaygroundSupport

class MyViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        // Main view
        view.backgroundColor = .black
        view.frame = CGRect(x: 0, y: 0, width: 500, height: 500)

        // Red view
        let redView = UIView(frame: CGRect(x: 20, y: 20, width: 460, height: 460))
        redView.backgroundColor = .red
        view.addSubview(redView)

        // Blue view
        let blueView = UIView(frame: CGRect(x: 20, y: 20, width: 420, height: 420))
        blueView.backgroundColor = .blue
        redView.addSubview(blueView)

        // Orange view
        let orangeView = UIView(frame: CGRect(x: 20, y: 20, width: 380, height: 380))
        orangeView.backgroundColor = .orange
        blueView.addSubview(orangeView)

        // Yellow view
        let yellowView = UIView(frame: CGRect(x: 20, y: 20, width: 340, height: 100))
        yellowView.backgroundColor = .yellow
        orangeView.addSubview(yellowView)


        // Let's try to convert now
        var resultFrame = CGRect.zero
        let randomRect: CGRect = CGRect(x: 0, y: 0, width: 100, height: 50)

        /*
        func convert(CGRect, from: UIView?)
        Converts a rectangle from the coordinate system of another view to that of the receiver.
        */

        // The following line converts a rectangle (randomRect) from the coordinate system of yellowView to that of self.view:
        resultFrame = view.convert(randomRect, from: yellowView)

        // Try also one of the following to get a feeling of how it works:
        // resultFrame = view.convert(randomRect, from: orangeView)
        // resultFrame = view.convert(randomRect, from: redView)
        // resultFrame = view.convert(randomRect, from: nil)

        /*
        func convert(CGRect, to: UIView?)
        Converts a rectangle from the receiver’s coordinate system to that of another view.
        */

        // The following line converts a rectangle (randomRect) from the coordinate system of yellowView to that of self.view
        resultFrame = yellowView.convert(randomRect, to: view)
        // Same as what we did above, using "from:"
        // resultFrame = view.convert(randomRect, from: yellowView)

        // Also try:
        // resultFrame = orangeView.convert(randomRect, to: view)
        // resultFrame = redView.convert(randomRect, to: view)
        // resultFrame = orangeView.convert(randomRect, to: nil)


        // Add an overlay with the calculated frame to self.view
        let overlay = UIView(frame: resultFrame)
        overlay.backgroundColor = UIColor(white: 1.0, alpha: 0.9)
        overlay.layer.borderColor = UIColor.black.cgColor
        overlay.layer.borderWidth = 1.0
        view.addSubview(overlay)
    }
}

var ctrl = MyViewController()
PlaygroundPage.current.liveView = ctrl.view

ビューを表示するには、必ずアシスタントエディター()を表示してください。次のようになります。

ここに画像の説明を入力してください

ここまたはこの要旨で、より多くの例を貢献してください。


ありがとう、これはコンバージョンを理解するのに本当に役立ちました。
アナンド、

素晴らしい概観。ただしself.view、これらは冗長であり、必要でないview場合selfは簡単に使用できると思います。
JakubTruhlář2017年

@JakubTruhlářのおかげで、削除しましたself
phi

どうもありがとう!あなたの遊び場は、この変換がどのように機能するかを理解するのに大いに役立ちました。
Anvar Azizov

30

これはわかりやすい英語での説明です。

サブビュー(aViewのサブビュー[aView superview])の四角形を別のビュー(self)の座標空間に変換する場合。

// So here I want to take some subview and put it in my view's coordinate space
_originalFrame = [[aView superview] convertRect: aView.frame toView: self];

3
これは真実ではありません。ビューを移動するのではなく、別の観点から見たビューの座標を提供するだけです。あなたの場合、それはあなたが彼らがどこにいるのかという点であなたにaViewの座標を与えます。
カール

正しい。ビューは移動しません。このメソッドはCGRectを返します。そのCGRectで行うことを選択するのはあなたのビジネスです。:-)上記の場合、画面上の視覚的な位置を維持しながら、あるビューを別のビューの階層に移動するために使用できます。
horseshoe7 14年

14

iOSのすべてのビューには座標系があります。座標系は、x軸(水平線)とy軸(垂直線)を持つグラフのようなものです。線が交差する点を原点と呼びます。点は(x、y)で表されます。たとえば、(2、1)は、ポイントが左に2ピクセル、下に1ピクセルであることを意味します。

座標系の詳細については、こちらをご覧ください-http://en.wikipedia.org/wiki/Coordinate_system

しかし、知っておく必要があるのは、iOSでは、すべてのビューに独自の座標系があり、左上隅が原点であるということです。X軸は右に向かって増加し、y軸は下に向かって増加します。

変換ポイントの質問については、次の例を見てください。

幅100ピクセル、高さ100ピクセルのV1と呼ばれるビューがあります。今その中に、(10、10、50、50)にV2と呼ばれる別のビューがあります。これは、(10、10)がV2の左上隅が配置されるV1の座標系の点であることを意味します。 50、50)はV2の幅と高さです。さて、V2の座標系、たとえば(20、20)の内側の点を取り上げます。さて、その点はV1の座標系の内側には何でしょうか?これがメソッドの目的です(もちろん、自分で計算することもできますが、余分な作業を省くことができます)。レコードの場合、V1のポイントは(30、30)になります。

お役に立てれば。


9

質問と回答を投稿してくれてありがとう:これは私がこれを整理するのに役立ちました。

私のビューコントローラーには通常のビューがあります。

そのビューの内部には、子ビューに自動レイアウト制約とのクリーンな相互作用を提供する以外に機能しないグループ化ビューがいくつかあります。

これらのグループ化ビューの1つには、ユーザーが情報を入力するポップオーバービューコントローラーを表示する[追加]ボタンがあります。

view
--groupingView
----addButton

デバイスの回転中に、ビューコントローラーは、UIPopoverViewControllerDelegate呼び出しpopoverController:willRepositionPopoverToRect:inViewを介してアラートが発生します。

- (void)popoverController:(UIPopoverController *)popoverController willRepositionPopoverToRect:(inout CGRect *)rect inView:(inout UIView *__autoreleasing *)view
{
    *rect = [self.addButton convertRect:self.addbutton.bounds toView:*view];
}

上記の最初の2つの回答で与えられた説明に由来する本質的な部分は、私が変換する必要がある四角形が、フレームではなく追加ボタンの境界であることでした。

もっと複雑なビュー階層でこれを試したことはありませんが、メソッド呼び出し(inView :)で提供されるビューを使用することで、多層リーフビューの複雑さの複雑さを回避できると思います。


3
「変換する必要があったのは、フレームではなく、追加ボタンの境界でした。」-機能させるために、多くの醜いフレームコードから私を本質的に救いました。この指摘して時間を割いてくれてありがとう
latenitecoder

3

私の場合は、この投稿を使用して応募しました。これが将来別の読者に役立つことを願っています。

ビューは、直接の子ビューと親ビューのみを表示できます。祖父母や孫の視界は見えません。

だから、私の場合、私はと呼ばれる壮大な親ビューを持ってself.view、この中で、self.view私はと呼ばれるサブビューを追加しましたself.child1OfViewself.child2OfView。でself.child1OfView、私はと呼ばれるサブビューを追加していますself.child1OfView1self.child2OfView1
今、私は物理的に移動した場合self.child1OfView1の境界の外側の領域へself.child1OfViewの葯スポットへのself.viewその後のための新しい位置を計算するために、self.child1OfView1self.view:

CGPoint newPoint = [self.view convertPoint:self.child1OfView1.center fromView:self.child1OfView];

3

以下のコードを見ると、実際にどのように機能するかを理解できます。

    let scrollViewTemp = UIScrollView.init(frame: CGRect.init(x: 10, y: 10, width: deviceWidth - 20, height: deviceHeight - 20))

override func viewDidLoad() {
    super.viewDidLoad()

    scrollViewTemp.backgroundColor = UIColor.lightGray
    scrollViewTemp.contentSize = CGSize.init(width: 2000, height: 2000)
    self.view.addSubview(scrollViewTemp)

    let viewTemp = UIView.init(frame: CGRect.init(x: 100, y: 100, width: 150, height: 150))
    viewTemp.backgroundColor = UIColor.green
    self.view.addSubview(viewTemp)

    let viewSecond = UIView.init(frame: CGRect.init(x: 100, y: 700, width: 300, height: 300))
    viewSecond.backgroundColor = UIColor.red
    self.view.addSubview(viewSecond)

    self.view.convert(viewTemp.frame, from: scrollViewTemp)
    print(viewTemp.frame)


    /*  First take one point CGPoint(x: 10, y: 10) of viewTemp frame,then give distance from viewSecond frame to this point.
     */
    let point = viewSecond.convert(CGPoint(x: 10, y: 10), from: viewTemp)
    //output:   (10.0, -190.0)
    print(point)

    /*  First take one point CGPoint(x: 10, y: 10) of viewSecond frame,then give distance from viewTemp frame to this point.
     */
    let point1 = viewSecond.convert(CGPoint(x: 10, y: 10), to: viewTemp)
    //output:  (10.0, 210.0)
    print(point1)

    /*  First take one rect CGRect(x: 10, y: 10, width: 20, height: 20) of viewSecond frame,then give distance from viewTemp frame to this rect.
     */
    let rect1 = viewSecond.convert(CGRect(x: 10, y: 10, width: 20, height: 20), to: viewTemp)
    //output:  (10.0, 210.0, 20.0, 20.0)
    print(rect1)

    /* First take one rect CGRect(x: 10, y: 10, width: 20, height: 20) of viewTemp frame,then give distance from viewSecond frame to this rect.
     */
    let rect = viewSecond.convert(CGRect(x: 10, y: 10, width: 20, height: 20), from: viewTemp)
    //output:  (10.0, -190.0, 20.0, 20.0)
    print(rect)

}

1

私は答えを読んでメカニズムを理解していますが、最後の例は正しくないと思います。APIドキュメントによると、ビューの中央プロパティには、スーパービューの座標系でのビューの既知の中心点が含まれています。

この場合、値がサブビューの座標系にないため、スーパービューにサブビューの座標系からサブビューの中心を変換するよう依頼することは意味がないと思います。理にかなっているのは、逆のこと、つまり、スーパービューの座標系からサブビューの座標系に変換することです...

あなたは2つの方法でそれを行うことができます(どちらも同じ値をもたらすはずです):

CGPoint centerInSubview = [subview convertPoint:subview.center fromView:subview.superview];

または

CGPoint centerInSubview = [subview.superview convertPoint:subview.center toView:subview];

これがどのように機能するかを理解するのは私ですか?


私の例は正しくありませんでした。答えを更新しました。あなたが言うように、centerプロパティはすでにスーパービューの座標空間にあります。
jrturton 2012年

1

これらのAPIの使用に関するもう1つの重要なポイント。親ビューチェーンが、変換している四角形とto / fromビューの間で完全であることを確認してください。例-aView、bView、cView-

  • aViewはbViewのサブビューです
  • aView.frameをcViewに変換したい

bViewがcViewのサブビューとして追加される前にメソッドを実行しようとすると、bunk応答が返されます。残念ながら、この場合のメソッドに組み込まれた保護はありません。これは明白に思えるかもしれませんが、変換が親の長いチェーンを通過する場合に注意する必要があります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.