ios 了解 convertRect:toView:、convertRect:FromView:、convertPoint:toView: 和 convertPoint:fromView: 方法
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/8465659/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Understand convertRect:toView:, convertRect:FromView:, convertPoint:toView: and convertPoint:fromView: methods
提问by Lorenzo B
I'm trying to understand the functionalities of these methods. Could you provide me a simple usecase to understand theirs semantics?
我试图了解这些方法的功能。你能给我一个简单的用例来理解他们的语义吗?
From the documentation, for example, convertPoint:fromView:method is described as follows:
例如,从文档中,convertPoint:fromView:方法描述如下:
Converts a point from the coordinate system of a given view to that of the receiver.
将一个点从给定视图的坐标系转换为接收器的坐标系。
What does the coordinate systemmean? What about the receiver?
什么是坐标系是什么意思?怎么样接收器?
For example, does it make sense using convertPoint:fromView:like the following?
例如,使用convertPoint:fromView:像下面这样有意义吗?
CGPoint p = [view1 convertPoint:view1.center fromView:view1];
Using NSLog utility, I've verified that p value coincides with view1's center.
使用 NSLog 实用程序,我已经验证 p 值与 view1 的中心重合。
Thank you in advance.
先感谢您。
EDIT:for those interested in, I've created a simple code snippet to understand these methods.
编辑:对于那些感兴趣的人,我创建了一个简单的代码片段来理解这些方法。
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));
In both cases self.window is the receiver. But there is a difference. In the first case the convertPoint parameter is expressed in view1 coordinates. The output is the following:
在这两种情况下 self.window 都是接收者。但有一个区别。在第一种情况下,convertPoint 参数以 view1 坐标表示。输出如下:
convertPoint:fromView: {100, 100}
转换点:从视图:{100, 100}
In the second one, instead, the convertPoint is expressed in superview (self.window) coordinates. The output is the following:
相反,在第二个中,convertPoint 以父视图 (self.window) 坐标表示。输出如下:
convertPoint:toView: {0, 0}
转换点:到视图:{0, 0}
回答by jrturton
Each view has its own coordinate system - with an origin at 0,0 and a width and height. This is described in the bounds
rectangle of the view. The frame
of the view, however, will have its origin at the point within the bounds rectangle of its superview.
每个视图都有自己的坐标系——原点为 0,0,宽度和高度。这bounds
在视图的矩形中进行了描述。该frame
视图的,但是,将有它的起源点内的边界矩形它的父的。
The outermost view of your view hierarchy has it's origin at 0,0 which corresponds to the top left of the screen in iOS.
视图层次结构的最外层视图的原点为 0,0,对应于 iOS 屏幕的左上角。
If you add a subview at 20,30 to this view, then a point at 0,0 in the subview corresponds to a point at 20,30 in the superview. This conversion is what those methods are doing.
如果您在 20,30 处向该视图添加子视图,则子视图中 0,0 处的点对应于超视图中 20,30 处的点。这种转换就是这些方法所做的。
Your example above is pointless (no pun intended) since it converts a point from a view to itself, so nothing will happen. You would more commonly find out where some point of a view was in relation to its superview - to test if a view was moving off the screen, for example:
你上面的例子毫无意义(没有双关语),因为它将一个点从一个观点转换为它自己,所以什么都不会发生。您通常会找出视图的某个点与其父视图相关的位置 - 以测试视图是否移出屏幕,例如:
CGPoint originInSuperview = [superview convertPoint:CGPointZero fromView:subview];
The "receiver" is a standard objective-c term for the object that is receiving the message (methods are also known as messages) so in my example here the receiver is superview
.
“接收器”是用于接收消息的对象的标准 Objective-c 术语(方法也称为消息),因此在我的示例中,接收器是superview
.
回答by phi
I always find this confusing so I made a playground where you can visually explore what the convert
function does. This is done in Swift 3 and Xcode 8.1b:
我总是觉得这很令人困惑,所以我创建了一个游乐场,您可以在其中直观地探索该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
Remember to show the Assistant Editor (???) in order to see the views, it should look like this:
请记住显示助理编辑器 ( ???) 以查看视图,它应该如下所示:
Feel free to contribute more examples here or in this gist.
请随意在此处或在此要点中贡献更多示例。
回答by horseshoe7
Here's an explanation in plain English.
这是一个简单的英语解释。
When you want to convert the rect of a subview (aView
is a subview of [aView superview]
) to the coordinate space of another view (self
).
当您想将一个子视图(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];
回答by Saurav Sachidanand
Every view in iOS have a coordinate system. A coordinate system is just like a graph, which has x axis(horizontal line) and y axis(vertical line). The point at which the lines interesect is called origin. A point is represented by (x, y). For example, (2, 1) means that the point is 2 pixels left, and 1 pixel down.
iOS 中的每个视图都有一个坐标系。坐标系就像一个图形,它有 x 轴(水平线)和 y 轴(垂直线)。线相交的点称为原点。一个点由 (x, y) 表示。例如,(2, 1) 表示该点向左 2 个像素,向下 1 个像素。
You can read up more about coordinate systems here - http://en.wikipedia.org/wiki/Coordinate_system
您可以在此处阅读有关坐标系的更多信息 - http://en.wikipedia.org/wiki/Coordinate_system
But what you need to know is that, in iOS, every view has it's OWN coordinate system, where the top left corner is the origin. X axis goes on increasing to the right, and y axis goes on increasing down.
但是你需要知道的是,在 iOS 中,每个视图都有它自己的坐标系,左上角是原点。X 轴继续向右增加,y 轴继续向下增加。
For the converting points question, take this example.
对于转换点问题,以这个例子为例。
There is a view, called V1, which is 100 pixels wide and 100 pixels high. Now inside that, there is another view, called V2, at (10, 10, 50, 50) which means that (10, 10) is the point in V1's coordinate system where the top left corner of V2 should be located, and (50, 50) is the width and height of V2. Now, take a point INSIDE V2's coordinate system, say (20, 20). Now, what would that point be inside V1's coordinate system? That is what the methods are for(of course you can calculate themselves, but they save you extra work). For the record, the point in V1 would be (30, 30).
有一个视图,称为 V1,它宽 100 像素,高 100 像素。现在在其中,还有另一个视图,称为 V2,位于 (10, 10, 50, 50) 处,这意味着 (10, 10) 是 V1 坐标系中 V2 左上角应位于的点,并且( 50, 50) 是 V2 的宽度和高度。现在,在 V2 的坐标系中取一个点,例如 (20, 20)。现在,那个点在 V1 的坐标系内是什么?这就是这些方法的用途(当然您可以自己计算,但它们可以节省您的额外工作)。作为记录,V1 中的点将是 (30, 30)。
Hope this helps.
希望这可以帮助。
回答by tobinjim
Thank you all for posting the question and your answers: It helped me get this sorted out.
感谢大家发布问题和答案:它帮助我解决了这个问题。
My view controller has it's normal view.
我的视图控制器有它的正常视图。
Inside that view there are a number of grouping views that do little more than give their child views a clean interaction with auto layout constraints.
在该视图中,有许多分组视图,它们的作用只不过是让它们的子视图与自动布局约束进行干净的交互。
Inside one of those grouping views I have an Add button that presents a popover view controller where the user enters some information.
在这些分组视图之一中,我有一个添加按钮,它显示了一个弹出视图控制器,用户可以在其中输入一些信息。
view
--groupingView
----addButton
During device rotation the view controller is alerted via the UIPopoverViewControllerDelegate call popoverController:willRepositionPopoverToRect:inView:
在设备旋转期间,视图控制器通过 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];
}
The essential part that comes from the explanation given by the first two answers above was that the rect I needed to convert from was the boundsof the add button, not its frame.
来自上面前两个答案给出的解释的重要部分是,我需要转换的矩形是添加按钮的边界,而不是它的框架。
I haven't tried this with a more complex view hierarchy, but I suspect that by using the view supplied in the method call (inView:) we get around the complications of multi-tiered leaf view kinds of ugliness.
我还没有尝试过使用更复杂的视图层次结构,但我怀疑通过使用方法调用 (inView:) 中提供的视图,我们可以解决多层叶视图的复杂性。
回答by user523234
I used this post to apply in my case. Hope this will help another reader in the future.
我用这篇文章来申请我的案例。希望这对未来的其他读者有所帮助。
A view can only see its immediate children and parent views. It can't see its grand parents or its grandchildren views.
一个视图只能看到它的直接子视图和父视图。它看不到它的祖父母或它的孙子视图。
So, in my case, I have a grand parent view called self.view
, in this self.view
I have added subviews called self.child1OfView
, self.child2OfView
. In self.child1OfView
, I have added subviews called self.child1OfView1
, self.child2OfView1
.
Now if I physically move self.child1OfView1
to an area outside the boundary of self.child1OfView
to anther spot on self.view
, then to calculator the new position for the self.child1OfView1
within the self.view:
因此,就我而言,我有一个名为 的祖父视图self.view
,在此self.view
我添加了名为self.child1OfView
, 的子视图self.child2OfView
。在 中self.child1OfView
,我添加了名为self.child1OfView1
, 的子视图self.child2OfView1
。
现在,如果我物理移动self.child1OfView1
到self.child1OfView
花药点边界外的区域self.view
,然后计算该区self.child1OfView1
域内的新位置self.view:
CGPoint newPoint = [self.view convertPoint:self.child1OfView1.center fromView:self.child1OfView];
回答by vikas prajapati
You can see below code so you can understand that how it actually works.
您可以查看下面的代码,以便了解它的实际工作原理。
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)
}
回答by D-avid
One more important point about using these APIs. Be sure that the parent view chain is complete between the rect you are converting and the to/from view. For example - aView, bView, and cView -
关于使用这些 API 的另一点很重要。确保父视图链在您正在转换的矩形和到/来自视图之间是完整的。例如 - aView、bView 和 cView -
- aView is a subview of bView
- we want to convert aView.frame to cView
- aView 是 bView 的子视图
- 我们想将 aView.frame 转换为 cView
If we try to execute the method before bView has been added as a subview of cView, we will get back a bunk response. Unfortunately there is no protection built into the methods for this case. This may seem obvious, but it is something to be aware of in cases where the conversion goes through a long chain of parents.
如果我们在将 bView 添加为 cView 的子视图之前尝试执行该方法,我们将返回一个 bunk 响应。不幸的是,这种情况下的方法没有内置保护。这可能看起来很明显,但在转换经过一长串父母的情况下,这是需要注意的。
回答by Moonwalker
I read the answer and understand the mechanics but I think the final example is not correct. According to the API doc, the center property of a view contains the known center point of the view in the superview's coordinate system.
我阅读了答案并了解了机制,但我认为最后一个例子是不正确的。根据 API 文档,视图的 center 属性包含视图在父视图坐标系中的已知中心点。
If this is the case, than I think it would not make sense to try to ask the superview to convert the center of a subview FROM the subview coordinate system because the value is not in the subview coordinate system. What would make sense is to do the opposite i.e. convert from the superview coordinate system to that of a subview...
如果是这种情况,我认为尝试要求超级视图从子视图坐标系转换子视图的中心是没有意义的,因为该值不在子视图坐标系中。做相反的事情是有意义的,即从超视图坐标系转换为子视图的坐标系......
You can do it in two ways (both should yield the same value):
您可以通过两种方式执行此操作(两者都应产生相同的值):
CGPoint centerInSubview = [subview convertPoint:subview.center fromView:subview.superview];
or
或者
CGPoint centerInSubview = [subview.superview convertPoint:subview.center toView:subview];
Am I way off in understanding how this should work?
我在理解这应该如何工作方面还有路要走吗?