example cgaffinetransformrotate iphone ios objective-c uiview cgaffinetransform

iphone - cgaffinetransformrotate - Encontrar coordenadas de cuadro después de aplicar la transformación UIView(CGAffineTransform)



cgaffinetransformrotate swift 3 (6)

¿Por qué el lío y el alboroto? ¿Mantenlo simple? Donde x estaba antes de la transformación, será q rads / grados más allá, como ocurre con cada otro punto alrededor del ancla.

Lo iba a explicar todo, pero este capítulo en esta publicación lo explicó en un contexto aún más corto:

Obtener el ángulo / rotación / radian actual para una UIview?

CGFloat radians = atan2f(yourView.transform.b, yourView.transform.a); CGFloat degrees = radians * (180 / M_PI);

CGAffineTransform mi vista con CGAffineTransform

[view setTransform:newTransform];

Los valores del cuadro siguen siendo los mismos después de que se aplica la transformación, pero ¿cómo encuentro valores "rotados" o transformados de este cuadro?

http://www.informit.com/content/images/art_sadun_affine/elementLinks/sadun_fig02.png

Quiero las coordenadas exactas de los bordes del marco girado, es decir, los puntos a, b, c, d.


Deberías usar:

CGPoint CGPointApplyAffineTransform ( CGPoint point, CGAffineTransform t );

Para obtener un punto específico, use los límites y el centro de la vista, y luego aplique la transformación de la vista para obtener un nuevo punto después de la transformación. Esto es mejor que agregar código específicamente para la transformación de rotación, ya que puede admitir cualquier transformación así como también el encadenamiento.


Escribí esta clase que nos puede ayudar:

TransformedViewFrameCalculator.h

#import <Foundation/Foundation.h> @interface TransformedViewFrameCalculator : NSObject @property (nonatomic, strong) UIView *viewToProcess; - (void)calculateTransformedCornersWithTranslation:(CGPoint)translation scale:(CGFloat)scale rotation:(CGFloat)rotation; @property (nonatomic, readonly) CGPoint transformedTopLeftCorner; @property (nonatomic, readonly) CGPoint transformedTopRightCorner; @property (nonatomic, readonly) CGPoint transformedBottomLeftCorner; @property (nonatomic, readonly) CGPoint transformedBottomRightCorner; @end

TransformedViewFrameCalculator.m:

#import "TransformedViewFrameCalculator.h" @interface TransformedViewFrameCalculator () @property (nonatomic, assign) CGRect viewToProcessNotTransformedFrame; @property (nonatomic, assign) CGPoint viewToProcessNotTransformedCenter; @end @implementation TransformedViewFrameCalculator - (void)setViewToProcess:(UIView *)viewToProcess { _viewToProcess = viewToProcess; CGAffineTransform t = _viewToProcess.transform; _viewToProcess.transform = CGAffineTransformIdentity; _viewToProcessNotTransformedFrame = _viewToProcess.frame; _viewToProcessNotTransformedCenter = _viewToProcess.center; _viewToProcess.transform = t; } - (void)calculateTransformedCornersWithTranslation:(CGPoint)translation scale:(CGFloat)scale rotation:(CGFloat)rotation { double viewWidth = _viewToProcessNotTransformedFrame.size.width * scale; double viewHeight = _viewToProcessNotTransformedFrame.size.height * scale; CGPoint viewCenter = CGPointMake(_viewToProcessNotTransformedCenter.x + translation.x, _viewToProcessNotTransformedCenter.y + translation.y); _transformedTopLeftCorner = [self calculateCoordinatesForViewPoint:CGPointMake(0, 0) fromViewCenter:viewCenter viewWidth:viewWidth viewHeight:viewHeight angleOfRotation:rotation]; _transformedTopRightCorner = [self calculateCoordinatesForViewPoint:CGPointMake(0, viewHeight) fromViewCenter:viewCenter viewWidth:viewWidth viewHeight:viewHeight angleOfRotation:rotation]; _transformedBottomLeftCorner = [self calculateCoordinatesForViewPoint:CGPointMake(viewWidth, 0) fromViewCenter:viewCenter viewWidth:viewWidth viewHeight:viewHeight angleOfRotation:rotation]; _transformedBottomRightCorner = [self calculateCoordinatesForViewPoint:CGPointMake(viewWidth, viewHeight) fromViewCenter:viewCenter viewWidth:viewWidth viewHeight:viewHeight angleOfRotation:rotation]; } - (CGPoint)calculateCoordinatesForViewPoint:(CGPoint)viewPoint fromViewCenter:(CGPoint)viewCenter viewWidth:(CGFloat)viewWidth viewHeight:(CGFloat)viewHeight angleOfRotation:(CGFloat)angleOfRotation { CGPoint centeredViewPoint = CGPointMake(viewPoint.x - viewWidth/2.0, viewPoint.y - viewHeight/2.0); CGPoint rotatedCenteredViewPoint = CGPointApplyAffineTransform(centeredViewPoint, CGAffineTransformMakeRotation(angleOfRotation)); CGPoint rotatedViewPoint = CGPointMake(rotatedCenteredViewPoint.x + viewCenter.x, rotatedCenteredViewPoint.y + viewCenter.y); return rotatedViewPoint; }

Por ejemplo, lo uso para restringir el movimiento / escala / rotación de una etiqueta dentro de una vista de contenedor de la siguiente manera:

@property (nonatomic, strong) TransformedViewFrameCalculator *transformedFrameCalculator;

...

self.transformedFrameCalculator = [TransformedViewFrameCalculator new]; self.transformedFrameCalculator.viewToProcess = someView;

...

- (BOOL)transformedView:(UIView *)view withTranslation:(CGPoint)translation scale:(double)scale rotation:(double)rotation isFullyInsideValidFrame:(CGRect)validFrame { [self.transformedFrameCalculator calculateTransformedCornersWithTranslation:translation scale:scale BOOL topRightIsInsideValidFrame = CGRectContainsPoint(validFrame, self.transformedFrameCalculator.transformedTopRightCorner); BOOL topLeftIsInsideValidFrame = CGRectContainsPoint(validFrame, self.transformedFrameCalculator.transformedTopLeftCorner); BOOL bottomRightIsInsideValidFrame = CGRectContainsPoint(validFrame, self.transformedFrameCalculator.transformedBottomRightCorner); BOOL bottomLeftIsInsideValidFrame = CGRectContainsPoint(validFrame, self.transformedFrameCalculator.transformedBottomLeftCorner); return topRightIsInsideValidFrame && topLeftIsInsideValidFrame && bottomRightIsInsideValidFrame && bottomLeftIsInsideValidFrame; }


Prueba este codigo

CGPoint localBeforeTransform = CGPointMake( view.bounds.size.width/2.0f, view.bounds.size.height/2.0f ); // lower left corner CGPoint localAfterTransform = CGPointApplyAffineTransform(localBeforeTransform, transform); CGPoint globalAfterTransform = CGPointMake(localAfterTransform.x + view.center.x, localAfterTransform.y + view.center.y);


Puede averiguar las coordenadas de la vista rotada utilizando la trigonometría básica. Aquí sabrás como podrás hacerlo:

  1. El primer paso es conocer el ancho y la altura de tu vista. Divídelos por 2 y obtendrás los lados adyacentes y opuestos de tu triángulo (cian y verde respectivamente). En el ejemplo anterior, el ancho = 300 y la altura = 300. Así que adyacente al lado = 150 y al lado opuesto = 150.

  2. Encuentra la hipotenusa (roja). Para esto usas la fórmula: h^2 = a^2 + b^2 . Después de aplicar esta fórmula encontramos la hipotenusa = 212.13.

  3. Encontrar theta Este es el ángulo entre el lado adyacente (cian) y la hipotenusa (rojo). Para esto usas la fórmula cos(theta) = (h^2 + a^2 - o^2)/2*h*o . Después de aplicar esta fórmula encontramos que theta = 0.785 (RADIANES). Para convertir esto a grados, aplicamos la fórmula degrees = radians * 180 / PI = 45 (grados). Este es el ángulo inicial (offset) de la hipotenusa. Esto es muy importante para darse cuenta. SI LA ROTACIÓN DE LA VISTA DE SU VISTA ES CERO, LA HIPOTENJA TIENE UN ÁNGULO DE COMPENSACIÓN DE 45 (GRADOS). Vamos a necesitar theta en breve.

  4. Ahora que conocemos la hipotenusa (roja) necesitamos la rotación de ángulos. En este ejemplo usé un UIRotationGestureRecognizer para rotar la vista cuadrada. Esta clase tiene una propiedad de "rotación" que nos dice cuánto ha girado la vista. Este valor está en RADIANS. En el ejemplo anterior, la rotación es 0.3597 RADIANS. Para convertirlo en grados utilizamos la fórmula degrees = radians * PI / 180 . Después de aplicar la fórmula, encontramos que el ángulo de rotación es de 20.61 grados.

  5. Finalmente podemos encontrar el ancho de desplazamiento (naranja) y la altura (púrpura). Para el ancho usamos la fórmula width = cos(rotationAngle - theta) * hypotenuse y para la altura usamos la fórmula height = sen(rotationAngle - theta) . TENEMOS QUE SUSTITUIR A THETA (¡EN RADIANES!) DEL ÁNGULO DE ROTACIÓN (¡EN TAMBIÉN EN RADIANES!) PORQUE THETA ERA LA COMPENSACIÓN INICIAL. Míralo de esta manera: la hipotenusa tenía un ángulo de 45 (grados) cuando el ángulo de rotación era cero. Después de aplicar las fórmulas encontramos que ancho = 193.20 y altura = -87.60

  6. Finalmente, podemos agregar esos valores (ancho y alto) al centro del cuadrado para encontrar las coordenadas del punto azul.

-Ejemplo-

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

Las coordenadas del punto azul son (963.20, 272.40)

Para entender mejor las fórmulas por favor vea los siguientes enlaces:

Además, si desea jugar con el proyecto de prueba que creé (es el que está en la imagen), no dude en descargarlo desde el siguiente enlace .

ACTUALIZAR

Aquí hay un método condensado que calculará el punto de arriba a la derecha (azul) de compensación que está buscando.

/* 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; }

¡Espero que esto ayude!


Una cosa que se debe tener en cuenta es que la transformación cambia el sistema de coordenadas, por lo que tendrá que poder convertir entre la vista principal y la vista secundaria (transformada). Además, las transformaciones conservan el centro del objeto transformado pero no cualquiera de las otras coordenadas. Así que necesitas calcular las cosas en términos del centro. Y hay varios ayudantes que necesitarás. (Obtuve la mayor parte del siguiente enfoque del libro de Erica Sadun, Core iOS Developer''s Cookbook).

Por lo general agrego estos como una categoría en UIView.

Para transformar las coordenadas del niño a las del padre, necesita algo como:

// 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) } }