web-dev-qa-db-ja.com

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

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

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

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

座標系はどういう意味ですか? receiverについてはどうですか?

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

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

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

前もって感謝します。

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

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}

124
Lorenzo B

各ビューには独自の座標系があります-Originは0,0で、幅と高さがあります。これは、ビューのbounds長方形で説明されています。ただし、ビューのframeは、スーパービューの境界矩形内のポイントにOriginを持ちます。

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

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

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

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

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

178
jrturton

私はいつもこれを紛らわしいと思うので、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

アシスタントエディター()ビューを表示するには、次のようになります。

enter image description here

ここまたは this Gist でさらに例を提供してください。

34
phi

わかりやすい英語で説明します。

サブビューの四角形(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];
30
horseshoe7

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

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

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

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

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

お役に立てれば。

14

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

私のView Controllerには通常のビューがあります。

そのビュー内には、子ビューに自動レイアウト制約との明確な相互作用を与える以上のことを行う多くのグループ化ビューがあります。

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

view
--groupingView
----addButton

デバイスの回転中、View Controllerは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つの回答で与えられた説明から得られる重要な部分は、変換に必要な四角形がboundsフレームではなく、追加ボタンの。

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

8
tobinjim

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

    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)

}
3
vikas prajapati

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

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

そのため、私の場合、self.viewという上位の親ビューがあり、このself.viewにはself.child1OfViewself.child2OfViewというサブビューが追加されています。 self.child1OfViewに、self.child1OfView1self.child2OfView1というサブビューを追加しました。
今、self.child1OfView1の境界外の領域にself.child1OfViewを物理的に移動して、self.viewの別の場所に移動し、self.child1OfView1の新しい位置を計算する場合self.view:

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

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

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

次の2つの方法で行うことができます(どちらも同じ値を生成する必要があります)。

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

または

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

これがどのように機能するかを理解するのに道は外れていますか?

1
Moonwalker

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

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

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

1
D-avid