Buscar coorderadas de cuadro después de aplicar la transformación UIView (CGAffineTransform)

CGAffineTransform mi vista con CGAffineTransform

 [view setTransform:newTransform]; 

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

http://www.informit.com/contenthttp://sofes.miximages.comart_sadun_affine/elementLinks/sadun_fig02.png

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

Una cosa a tener en count es que la transformación cambia el sistema de coorderadas, por lo que deberá poder convertir entre la vista 'padre' y la vista secundaria (transformada). Además, las transformaciones conservan el centro del object transformado pero no ninguna de las otras coorderadas. Entonces, 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 Cookbook Developer Core iOS).

Generalmente añado esto como una categoría en UIView.

Para transformar las coorderadas 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]; } 

Puede encontrar las coorderadas de la vista girada usando trigonometría básica. Aquí sabrás como podrás hacerlo:

introduzca la descripción de la imagen aquí

  1. El primer paso es conocer el ancho y la altura de su vista. Divídalos por 2 y obtienes los lados adyacentes y opuestos de tu triángulo (cian y verde, respectivamente). En el ejemplo anterior, ancho = 300 y alto = 300. Entonces, el lado adyacente = 150 y el lado opuesto = 150.

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

  3. Encuentra theta. Este es el ángulo entre el lado adyacente (cian) y el hipotenusa (rojo). Para esto, usa 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 (RADIANS). Para convertir esto a grados aplicamos la fórmula degrees = radians * 180 / PI = 45 (grados). Este es el ángulo inicial (desplazamiento) de la hipotenusa. Esto es muy importante para darse count. SI LA ROTACIÓN DE LA VISTA DE SU VISTA ES CERO EL HYPOTENUSE TIENE UN ÁNGULO DE DESPLAZAMIENTO DE 45 (GRADOS). Vamos a necesitar theta en breve.

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

  5. Finalmente podemos encontrar el ancho de desplazamiento (naranja) y alto (violeta). 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 SUBRAYAR THETA (¡EN RADIANTE!) DESDE EL ÁNGULO DE LA ROTACIÓN (¡EN RADIANS TAMBIÉN!) PORQUE THETA ERA EL DESPACHO INICIAL. Véalo de esta manera: la hipotenusa tenía un ángulo de 45 (grados) cuando el ángulo de rotation era cero. Después de aplicar las fórmulas encontramos que width = 193.20 y height = -87.60

  6. Finalmente, podemos agregar esos valores (ancho y alto) al centro del cuadrado para encontrar las coorderadas 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 coorderadas del punto azul son (963.20, 272.40)

Para comprender mejor las fórmulas, consulte los siguientes enlaces:

  • Trigonometría 1
  • Trigonometría 2

Además, si quieres jugar con el proyecto de testing que creé (es el de la image), puedes downloadlo desde el siguiente enlace .

ACTUALIZAR

Aquí hay un método condensado que calculará el punto tope-derecho (azul) de desplazamiento 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. Eg: 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!

Deberías usar:

 CGPoint CGPointApplyAffineTransform ( CGPoint point, CGAffineTransform t ); 

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

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

¿Por qué el desorder y el alboroto? ¿Mantenlo simple? Donde x estaba antes de la transformación, será q rads / degrees más exactamente como cualquier otro punto alnetworkingedor del ancla.

iba a explicarlo todo, pero este miembro de esta publicación lo explicó en un context aún más breve:

¿Obtiene el ángulo / rotation / radian actual para una UIview?

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

Escribí esta class 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 centenetworkingViewPoint = CGPointMake(viewPoint.x - viewWidth/2.0, viewPoint.y - viewHeight/2.0); CGPoint rotatedCentenetworkingViewPoint = CGPointApplyAffineTransform(centenetworkingViewPoint, CGAffineTransformMakeRotation(angleOfRotation)); CGPoint rotatedViewPoint = CGPointMake(rotatedCentenetworkingViewPoint.x + viewCenter.x, rotatedCentenetworkingViewPoint.y + viewCenter.y); return rotatedViewPoint; } 

Por ejemplo, lo uso para restringir el movimiento / escala / rotation de una label 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; }