Understand Convertrect:Toview:, Convertrect:Fromview:, Convertpoint:Toview: and Convertpoint:Fromview: Methods

Understand convertRect:toView:, convertRect:FromView:, convertPoint:toView: and convertPoint:fromView: methods

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.

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.

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.

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.

Understanding convertPoint:toView:

Every UIView has its own coordinates system. So if you have a UIView_1 that contains another UIView_2, they both have a point (10,10) within them.

convertPoint:toView: allows the developer to take a point in one view and convert the point to another view coordinate system.

Example:
view1 contains view2. The top left corner of view2 is located at view1 point (10,10), or better to say view2.frame.orgin = {10,10}. That {10,10} is based in the view1 coordinate system. So far so good.

The user touches the view2 at point {20,20} inside the view2. Now those coordinates are in the view2 coordinate system. You can now use covertPoint:toView: to convert {20,20} into the coordinate system of view1. touchPoint = {20,20}

CGPoint pointInView1Coords = [view2 convertPoint:touchPoint toView:view1];

So now pointInView1Coords should be {30,30} in the view1 coordinate systems. Now that was just simple math on this example, but there are all sorts of things that contribute to the conversion. Transforms and scaling come to mind.

Read about UIView frame, bounds, and center. They are all related and they deal with coordinate systems for a view. Its confusing until you start doing stuff with them. Remember this frame and center are in the parent's coordinate system. Bounds is in the view's coordinate system.

John

Best choice when detecting whether a view's point is inside another view?

You should be aware of what each method is used for:

The docs for pointInside:withEvent: say

point | A point that is in the receiver’s local coordinate system (bounds).

Apart from that the method probably does simply call CGRectContainsPoint with the passed point and the bounds frame.

CGRectContainsPoint simply does the math of checking the x and y coordinates against the origin and size. Simple math, but once again both have to be in the same coordinate system since neither the rect nor the point contain any information about their respective coordinate system, they are absolute values.

The convert* functions offer you the ability to transform the CGPoint which is relative to the receiver's coordinate system into some other relative coordinate system.

To understand what each method is good for, you have to understand what the different CGRects mean, frame and bounds in particular: this answer is THE way to go for that.

After understanding what the different properties mean it should be easy to pick the correct function.

TL;DR: the convert* methods offer you the ability to transform CGPoints between coordinate systems. The other two options operate within one certain fixed coordinate system. It is your job to pick the one you need in your particular case.

For example you might have to transform a point first and then perform the hit detection.

What is the android equivalent of UIView's convertRect / convertPoint functions?

I don't think there is an equivalent as part of the sdk, but it seems like you could write your own implementation very easily using getLocationOnScreen:

public static Point convertPoint(Point fromPoint, View fromView, View toView){
int[] fromCoord = new int[2];
int[] toCoord = new int[2];
fromView.getLocationOnScreen(fromCoord);
toView.getLocationOnScreen(toCoord);

Point toPoint = new Point(fromCoord[0] - toCoord[0] + fromPoint.x,
fromCoord[1] - toCoord[1] + fromPoint.y);

return toPoint;
}

public static Rect convertRect(Rect fromRect, View fromView, View toView){
int[] fromCoord = new int[2];
int[] toCoord = new int[2];
fromView.getLocationOnScreen(fromCoord);
toView.getLocationOnScreen(toCoord);

int xShift = fromCoord[0] - toCoord[0];
int yShift = fromCoord[1] - toCoord[1];

Rect toRect = new Rect(fromRect.left + xShift, fromRect.top + yShift,
fromRect.right + xShift, fromRect.bottom + yShift);

return toRect;
}

CGRectIntersectsRect two frames from different view hierarchy

Given

UIButton *button;
UIView *viewTrashArea;

This line would return true if they intersect:

CGRectIntersectsRect([button convertRect:button.bounds toView:viewTrashArea], viewTrashArea.bounds);

Converting two UIViews to the same coordinate system when UIViews are in hierarchy

Your problem is that imageViewA.frame is in the geometry (coordinate system) of imageViewA.superview, but UIView.convert(_ rect: to view:) expects rect to be in the geometry of imageViewA.

UPDATE

The simplest solution is to convert imageViewA.bounds (which is in imageViewA's geometry) directly to imageViewB's geometry, and then see if it intersects imageViewB.bounds, which is also in imageViewB's geometry:

let aInB = imageViewA.convert(imageViewA.bounds, to: imageViewB)
if aInB.intersects(imageViewB.bounds) {
...

ORIGINAL

The simplest solution is to convert imageViewA.bounds, which is in imageViewA's own geometry:

let frameA = imageViewA.convert(imageViewA.bounds, to: self.view)
let frameB = imageViewB.convert(imageViewB.bounds, to: self.view)

Converting points between view coordinate systems

Yes, in an expression like subview.convert..., we call subview the "receiver" — it is the object to which the convert message is sent, so it receives that message.

Why doesn't subview.convert(subview.bounds, from: window) return {x 0 y 0 w 50 h 50 }?

The mental mistake you're making here is that you think subview.bounds somehow pins this value to subview. It doesn't. The first parameter is merely some numbers. The convert method doesn't know where you got those numbers from. That is why you have to tell it where you got them — with the second parameter:

  • If you say from:, you are saying, "I got these numbers from the second parameter's coordinate system".

  • If you say to:, you are saying, "I got these numbers from the receiver's coordinate system".

why does the instance the method belongs to make a difference in the result of the conversion

Because that is the other view in the story:

  • If you say to:, you are saying, "I got these numbers from the receiver's coordinate system, and I want you to convert them to the second parameter's coordinate system."

  • If you say from:, you are saying, "I got these numbers from the second parameter's coordinate system, and I want you to convert them to the receiver's coordinate system."

Remember, the first parameter is just some numbers. You can get them from anywhere you want, including the inside of your head, but you won't get the "right" answer if you lie about this in the rest of the expression — as some of your examples do.

What is UIViewControllerWrapperView?

This is a private view used by the framework. You're not supposed to modify it or whatsoever.



Related Topics



Leave a reply



Submit