理解convertRect:toView:,convertRect:FromView:,convertPoint:toView:和convertPoint:fromView:methods

时间:2011-12-11 16:44:21

标签: ios layout uiview

我正在尝试理解这些方法的功能。你能给我一个简单的用例来理解他们的语义吗?

从文档中,例如, 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}

在第二个中,convertPoint以superview(self.window)坐标表示。输出如下:

  

convertPoint:toView:{0,0}

9 个答案:

答案 0 :(得分:175)

每个视图都有自己的坐标系 - 原点为0,0,宽度和高度。这在视图的bounds矩形中描述。但是,视图的frame的原点将位于其超视图的边界矩形内。

视图层次结构的最外层视图的原点是0,0,它对应于iOS中屏幕的左上角。

如果在该视图中添加20,30的子视图,则子视图中的0,0点对应于超视图中的20,30点。这种转换就是那些方法正在做的。

上面的例子毫无意义(没有双关语)因为它将一个点从视图转换为自身,所以什么都不会发生。您更常见的是查看某个视图与其超级视图相关的位置 - 测试视图是否正在移出屏幕,例如:

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

“receiver”是接收消息的对象的标准objective-c术语(方法也称为消息)所以在我的例子中接收器是superview

答案 1 :(得分:30)

我总觉得这让人感到困惑所以我做了一个游乐场,你可以在视觉上探索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中提供更多示例。

答案 2 :(得分:29)

这是用简单的英语解释的。

如果要将子视图的矩形(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 :(得分:14)

iOS中的每个视图都有一个坐标系。坐标系就像一个图形,它有x轴(水平线)和y轴(垂直线)。线相互作用的点称为原点。点由(x,y)表示。例如,(2,1)表示该点为左2像素,向下1像素。

您可以在此处阅读有关坐标系的更多信息 - http://en.wikipedia.org/wiki/Coordinate_system

但你需要知道的是,在iOS中,每个视图都有它的OWN坐标系,左上角是原点。 X轴继续向右增加,y轴继续向下增加。

对于转换点问题,请参考此示例。

有一个名为V1的视图,宽100像素,高100像素。现在在其中,还有另一个视图,称为V2,位于(10,10,50,50),这意味着(10,10)是V1坐标系中V2的左上角应该位于的点,并且( 50,50)是V2的宽度和高度。现在,请注意INSIDE V2的坐标系,比如说(20,20)。现在,V1的坐标系内有什么意义呢?这就是方法的用途(当然你可以自己计算,但它们可以为你节省额外的工作)。为了记录,V1中的点将是(30,30)。

希望这有帮助。

答案 4 :(得分:8)

感谢大家发布问题和答案:这有助于我解决这个问题。

我的视图控制器有正常视图。

在该视图中,有许多分组视图只能为其子视图提供与自动布局约束的干净交互。

在其中一个分组视图中,我有一个Add按钮,用于显示一个弹出视图控制器,用户可以在其中输入一些信息。

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];
}

上面前两个答案给出的解释的基本部分是我需要转换的矩形是添加按钮的 界限 ,而不是它的框架。

我没有尝试使用更复杂的视图层次结构,但我怀疑通过使用方法调用(inView :)中提供的视图,我们可以解决多层叶视图中的各种丑陋问题。

答案 5 :(得分: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];

答案 6 :(得分: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)

}

答案 7 :(得分:1)

我阅读了答案并理解了机制,但我认为最后的例子不正确。根据API文档,视图的center属性包含superview坐标系中视图的已知中心点。

如果是这种情况,我认为尝试要求superview将子视图的中心从子视图坐标系转换是没有意义的,因为该值不在子视图坐标系中。有意义的是做相反的事情,即从超视图坐标系转换为子视图的坐标系......

你可以用两种方式做到(两者都应该产生相同的值):

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

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

我是否理解这应该如何运作?

答案 8 :(得分:1)

使用这些API的另一个重点。确保在您要转换的矩形和转到/从视图之间完成父视图链。 例如 - aView,bView和cView -

  • aView是bView
  • 的子视图
  • 我们想将aView.frame转换为cView

如果我们尝试在将bView添加为cView的子视图之前执行该方法,我们将返回一个bunk响应。不幸的是,这种情况的方法没有内置保护。这似乎是显而易见的,但在转换经过长链父母的情况下,需要注意的事项。