理解 ConvertRect: toView: ,ConvertRect: FromView: ,ConvertPoint: toView: 和 ConvertPoint: from View: method

我试图理解这些方法的功能。你能为我提供一个简单的用例来理解他们的语义吗?

例如,根据文档,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: from View: {100,100}

相反,在第二个函数中,convertPoint以 Superview (self.window)坐标表示:

转换点: toView: {0,0}

79537 次浏览

每个视图都有自己的坐标系-原点为0,0,宽度和高度。这在视图的 bounds矩形中进行了描述。然而,视图的 frame将在其超视图的边界矩形内的点上有其原点。

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

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

上面的例子毫无意义(没有双关意) ,因为它将一个点从一个视图转换为它自己,所以什么也不会发生。你通常会找出某个视点与它的超视图相关的位置——测试一个视图是否正在离开屏幕,例如:

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

“接收方”是一个标准的 object-c 术语,表示接收消息的对象(方法也称为消息) ,因此在我的示例中,接收方是 superview

iOS 中的每一个视图都有一个坐标系。坐标系就像一个图形,有 x 轴(水平线)和 y 轴(垂直线)。两条直线相交的点称为原点。一个点由(x,y)表示。例如,(2,1)意味着点是2像素左,1像素下。

你可以在这里阅读更多关于坐标系的内容 -http://en.wikipedia.org/wiki/Coordinate_system

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

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

有一个视图,称为 V1,它是100像素宽和100像素高。在这个视图中,还有另一个视图,叫做 V2,在(10,10,50,50) ,这意味着(10,10)是 v1坐标系中 V2的左上角应该位于的位置,(50,50)是 v2的宽度和高度。现在,在 v2的坐标系内取一个点,比如(20,20)。那么,在 v1的坐标系里面会有什么点呢?这就是这些方法的用途(当然,您可以计算它们自己,但是它们可以为您节省额外的工作)。根据记录,V1中的点应该是(30,30)。

希望这个能帮上忙。

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

如果是这样的话,那么我认为试图要求 superview 从 subview 坐标系转换 subview 的中心是没有意义的,因为这个值不在 subview 坐标系中。我们可以反其道而行之,也就是说,从“监督”坐标系转换为“监督”... ..。

您可以通过两种方式进行操作(两种方式都应该产生相同的值) :

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

或者

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

我是不是理解错了?

我用这篇文章来申请我的案例,希望对以后的读者有所帮助。

视图只能看到其直接子视图和父视图。它看不到自己的祖父母或孙辈的观点。

因此,在我的例子中,我有一个叫做 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];

这里有一个简单明了的解释。

当您想要将子视图的直观(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];

关于使用这些 API,还有一点很重要。确保父视图链在正在转换的 rect 和 to/from 视图之间是完整的。 例如-aView、 bView 和 cView-

  • AView 是 bView 的子视图
  • 我们想把 aView.frame 转换成 cView

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

感谢你们所有人提出的问题和你们的回答: 它帮助我解决了这个问题。

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

在这个视图内部有许多分组视图,它们的作用仅仅是给它们的子视图提供一个与自动布局约束的干净的交互。

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

view
--groupingView
----addButton

在设备旋转期间,视图控制器会通过 UIPopoverViewControllerGenerate 调用 popoverController: will Reposition tionPopoverToRect: inView:

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

上面前两个答案给出的解释中最重要的部分是我需要转换的是添加按钮的 边界,而不是它的框架。

我还没有在更复杂的视图层次结构中尝试过这种方法,但是我怀疑通过使用方法调用(inView:)中提供的视图,我们可以避免多层叶视图中各种丑陋的复杂情况。

我总是觉得这令人困惑,所以我创建了一个操场,在那里您可以直观地探索 convert函数的功能。这是在 Swift 3和 Xcode 8.1 b 中完成的:

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

请随意贡献更多的例子在这里或在 这个要点

您可以看到下面的代码,以便理解它实际上是如何工作的。

    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)


}