Find Frame Coordinates After UIView Transform is Applied (CGAffineTransform) Find Frame Coordinates After UIView Transform is Applied (CGAffineTransform) ios ios

Find Frame Coordinates After UIView Transform is Applied (CGAffineTransform)


One thing to keep in mind is that the transform changes the coordinate system, so you will need to be able to convert between the parent 'view' and the child (transformed) view. Also, transforms preserve the center of the transformed object but not any of the other coordinates. So you need to calculate things in terms of the center. And there are several helpers you will need. (I got most of the following approach from Erica Sadun's book Core iOS Developer's Cookbook).

I usually add these as a category on UIView.

In order to transform the child's coordinates to those of the parent you need something like:

// helper to get pre transform frame-(CGRect)originalFrame {   CGAffineTransform currentTransform = self.transform;   self.transform = CGAffineTransformIdentity;   CGRect originalFrame = self.frame;   self.transform = currentTransform;   return originalFrame;}// helper to get point offset from center-(CGPoint)centerOffset:(CGPoint)thePoint {    return CGPointMake(thePoint.x - self.center.x, thePoint.y - self.center.y);}// helper to get point back relative to center-(CGPoint)pointRelativeToCenter:(CGPoint)thePoint {  return CGPointMake(thePoint.x + self.center.x, thePoint.y + self.center.y);}// helper to get point relative to transformed coords-(CGPoint)newPointInView:(CGPoint)thePoint {    // get offset from center    CGPoint offset = [self centerOffset:thePoint];    // get transformed point    CGPoint transformedPoint = CGPointApplyAffineTransform(offset, self.transform);    // make relative to center    return [self pointRelativeToCenter:transformedPoint];}// now get your corners-(CGPoint)newTopLeft {    CGRect frame = [self originalFrame];    return [self newPointInView:frame.origin];}-(CGPoint)newTopRight {    CGRect frame = [self originalFrame];    CGPoint point = frame.origin;    point.x += frame.size.width;    return [self newPointInView:point];}-(CGPoint)newBottomLeft {    CGRect frame = [self originalFrame];    CGPoint point = frame.origin;    point.y += frame.size.height;    return [self newPointInView:point];}-(CGPoint)newBottomRight {    CGRect frame = [self originalFrame];    CGPoint point = frame.origin;    point.x += frame.size.width;    point.y += frame.size.height;    return [self newPointInView:point];}

Swift 4

extension UIView {    /// Helper to get pre transform frame    var originalFrame: CGRect {        let currentTransform = transform        transform = .identity        let originalFrame = frame        transform = currentTransform        return originalFrame    }    /// Helper to get point offset from center    func centerOffset(_ point: CGPoint) -> CGPoint {        return CGPoint(x: point.x - center.x, y: point.y - center.y)    }    /// Helper to get point back relative to center    func pointRelativeToCenter(_ point: CGPoint) -> CGPoint {        return CGPoint(x: point.x + center.x, y: point.y + center.y)    }    /// Helper to get point relative to transformed coords    func newPointInView(_ point: CGPoint) -> CGPoint {        // get offset from center        let offset = centerOffset(point)        // get transformed point        let transformedPoint = offset.applying(transform)        // make relative to center        return pointRelativeToCenter(transformedPoint)    }    var newTopLeft: CGPoint {        return newPointInView(originalFrame.origin)    }    var newTopRight: CGPoint {        var point = originalFrame.origin        point.x += originalFrame.width        return newPointInView(point)    }    var newBottomLeft: CGPoint {        var point = originalFrame.origin        point.y += originalFrame.height        return newPointInView(point)    }    var newBottomRight: CGPoint {        var point = originalFrame.origin        point.x += originalFrame.width        point.y += originalFrame.height        return newPointInView(point)    }}


You can find out the coordinates of the rotated view by using basic trigonometry. Here is how you can do it:

enter image description here

  1. The first step is to know your view's width and height. Divide them by 2 and you get your triangle's adjacent and opposite sides (cyan and green respectively). In the example above width = 300 and height = 300. So adjacentSide = 150 and oppositeSice = 150.

  2. Find the hypotenuse (red). For this you use the formula: h^2 = a^2 + b^2. After applying this formula we find the hypotenuse = 212.13

  3. Find theta. This is the angle between the adjacentSide (cyan) and the hypotenuse (red). For this you use the formula cos(theta) = (h^2 + a^2 - o^2)/2*h*o. After applying this formula we find that theta = 0.785 (RADIANS). To convert this to degrees we apply the formula degrees = radians * 180 / PI = 45 (degrees). This is the initial (offset) angle of the hypotenuse. This is very important to realize. IF THE VIEW'S ROTATION OF YOUR VIEW IS ZERO THE HYPOTENUSE HAS AN OFFSET ANGLE OF 45(DEGREES). We're going to need theta shortly.

  4. Now that we know the hypotenuse (red) we need the rotationAngle. In this example I used a UIRotationGestureRecognizer to rotate the square view. This class has a "rotation" property which tells us how much the view has rotated. This value is in RADIANS. In the example above the rotation is 0.3597 RADIANS. To convert it to degrees we use the formula degrees = radians * PI / 180. After applying the formula we find the rotation angle to be 20.61 degrees.

  5. We can finally find the offset width (orange) and height (purple). For width we use the formula width = cos(rotationAngle - theta) * hypotenuse and for height we use the formula height = sen(rotationAngle - theta). WE HAVE TO SUBTRACT THETA (IN RADIANS!) FROM THE ROTATION ANGLE (IN RADIANS TOO!) BECAUSE THETA WAS THE INITIAL OFFSET. View it this way: the hypotenuse had an angle of 45(degrees) when the rotation angle was zero. After applying the formulas we find that width = 193.20 and height = -87.60

  6. Finally, we can add those values (width and height) to the center of the square to find the coordinates of the blue point.

-Example-

// Get the center pointCGPoint squareCenter = self.squareView.center;// Get the blue point coordinatesCGPoint bluePointCoordinates = CGPointMake(squareCenter.x + width, squareCenter.y + height);

The blue point coordinates are (963.20, 272.40)

To better understand the formulas please see the following links:

Also, if you want to play around with the test project I created (it's the one in the image) please feel free to download it from the following link.

UPDATE

Here is a condensed method that will calculate the offset top-right point (blue) you're looking for.

/* Params:/  viewCenter:      The center point (in superView coordinates) of your view/  width:           The total width of your view/  height:          The total height of your view/  angleOfRotation: The rotation angle of your view. Can be either DEGREES or RADIANS/  degrees:         A boolean flag indicating whether 'angleOfRotation' is degrees/                   or radians. E.g.: If 'angleOfRotation' is expressed in degrees/                   this parameter must be 'YES'*/-(CGPoint)calculateTopRightCoordinatesFromViewCenter:(CGPoint)viewCenter viewWidth:(CGFloat)viewWidth viewHeight:(CGFloat)viewHeight angleOfRotation:(CGFloat)angleOfRotation degrees:(BOOL)degrees {    CGFloat adjacent = viewWidth/2.0;    CGFloat opposite = viewHeight/2.0;    CGFloat hipotenuse = sqrtf(powf(adjacent, 2.0) + pow(opposite, 2.0));    CGFloat thetaRad = acosf((powf(hipotenuse, 2.0) + powf(adjacent, 2.0) - pow(opposite, 2.0)) / (2 * hipotenuse * adjacent));    CGFloat angleRad = 0.0;    if (degrees) {        angleRad = angleOfRotation*M_PI/180.0;    } else {        angleRad = angleOfRotation;    }    CGFloat widthOffset = cosf(angleRad - thetaRad) * hipotenuse;    CGFloat heightOffset = sinf(angleRad - thetaRad) * hipotenuse;    CGPoint offsetPoint = CGPointMake(viewCenter.x + widthOffset, viewCenter.y + heightOffset);    return offsetPoint;}

Hope this helps!


You should use:

CGPoint CGPointApplyAffineTransform (   CGPoint point,   CGAffineTransform t);

To get a specific point, use the view's bounds and center, and then apply the view's transform to get a new point after transform. This is better than adding code specifically for rotation transform, as it can support any transform as well as chaining.