Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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!)

like image 731
toblerpwn Avatar asked Dec 25 '12 08:12

toblerpwn


1 Answers

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.

like image 79
CodaFi Avatar answered Oct 05 '22 12:10

CodaFi