toblerpwn toblerpwn - 3 months ago 8
Objective-C Question

How to use CGRectContainsPoint() with rotated UIView

I have a

UIView
, and the user can tap the
UIView
to 'select' or highlight the in-app 'thing' that it represents. I use
CGRectContainsPoint(thing.frame,tapPoint)
to achieve this, where
thing.frame
is the frame of the
UIView
, and
tapPoint
is the tapped point from a
UITapGestureRecognizer
. This works perfect.

..except when
UIView
is rotated by setting the
transform
property (with a
CGAffineTransform
value). When the
UIView
is rotated like this, the
frame
becomes a flat square that encapsulates the rotated view.

Here is an illustration of the problem (frame property is labeled A, and the visual
UIView
bounds
are labeled B):

When NOT Rotated

+------------------+
| A == B |
+------------------+


When Rotated

+-----------------+
| A . |
| . . |
| . . |
| . . |
| . B . |
| . . |
| . . |
| . |
+-----------------+


I want to capture taps that are within the bounds of rect
B
(the true bounds of
UIView
, rotated), but NOT when they're only within rect
A
(the value of the
frame
property of
UIView
) and not
B
.

How might I calculate whether a given tap point is within the true bounds/frame/borders of the rotated
UIView
? Is there a convenience method for this? Or would I need to calculate the coordinates and dimensions of
B
using my own geometry?

(If the latter, please include a suggestion so we can make the answer as complete as possible. Thanks!)

Answer

You're discovering the one fundamental stumbling block that everybody has when they first work for frame and bounds.

Frame is the smallest possible (non-rotated) rectangle in which a view fits in, taking into account transformation. Meaning, if you were to test for touches, you could log in the available space around the view so long as it was within that smallest possible rectangle.

For the visual, imagine the blue square is a transformed UIView. The blue border around the view represents it's frame. Notice how, even though the view is transformed, that it's frame remains un-transformed and in standard position. The green area represents the areas that are touchable if frame is passed instead of bounds:

frame

Bounds, on the other hand, represents the reciever's rectangle with respect to itself, taking into account transformations, and so testing for points in the view by passing bounds (after a -convertPoint:toView: call) will correctly return whether or not a given touch (point) intersects the view.

Comments