【问题标题】:Find Frame Coordinates After UIView Transform is Applied (CGAffineTransform)应用 UIView 变换后查找帧坐标 (CGAffineTransform)
【发布时间】:2013-10-31 15:43:12
【问题描述】:

我用CGAffineTransform 旋转我的视图

[view setTransform:newTransform];

应用变换后帧值保持不变,但我如何找到该帧的“旋转”或变换值?


(来源:informit.com

我想要旋转框架边缘的精确坐标,即 a、b、c、d 点。

【问题讨论】:

  • 你不需要做任何触发。您应该按照@LeoNatan @Bladebunny 等人的建议使用CGPointApplyAffineTransform()CGRectApplyAffineTransform()
  • 所有这些答案都是令人费解的,而且无缘无故,Apple 提供了一种优化的方法来在一行代码中执行此操作(从 iOS2 开始),我鼓励您更新您选择的答案:@987654323 @

标签: iphone ios objective-c uiview cgaffinetransform


【解决方案1】:

所有这些答案都是疯狂的,这太简单了......

CGPoint topLeft = [rotatedView convertPoint:CGPointMake(0, 0) toView:rotatedView.superview];

CGPoint topRight = [rotatedView convertPoint:CGPointMake(rotatedView.bounds.size.width, 0) toView:rotatedView.superview];

CGPoint bottomLeft = [rotatedView convertPoint:CGPointMake(0, rotatedView.bounds.size.height) toView:rotatedView.superview];

CGPoint bottomRight = [rotatedView convertPoint:CGPointMake(rotatedView.bounds.size.width, rotatedView.bounds.size.height) toView:rotatedView.superview];

【讨论】:

    【解决方案2】:

    要记住的一点是变换会改变坐标系,因此您需要能够在父“视图”和子(变换后的)视图之间进行转换。此外,变换保留变换对象的中心,但不保留任何其他坐标。因此,您需要根据中心来计算事物。您将需要几个助手。 (我从 Erica Sadun 的《Core iOS Developer's Cookbook》一书中获得了以下大部分方法。

    我通常将这些作为类别添加到 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)
        }
    }
    

    【讨论】:

    • 你能不能试着给出这个答案stackoverflow.com/questions/34763819/…
    • 这是否意味着(例如)CGAffineTransformTranslate 翻译对象的坐标系,而不是其坐标系中的对象?
    • @Bladebunny 你能告诉代码获取每边 TOP 、 RIGHT、 BOTTOM 、 LEFT 的中心点吗?
    【解决方案3】:

    我写了这个可以帮助我们的课程:

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

    【讨论】:

      【解决方案4】:

      为什么会乱成一团?把事情简单化? x 在变换之前的位置,它将进一步 q 弧度/度,就像锚点周围的每个其他点一样。

      本打算解释这一切,但这篇文章中的这一章用更短的上下文解释了它:

      Get the current angle/rotation/radian for a UIview?

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

      【讨论】:

        【解决方案5】:

        试试这个代码

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

        【讨论】:

          【解决方案6】:

          您可以使用基本三角法找出旋转视图的坐标。以下是你可以做到的:

          1. 第一步是了解视图的宽度和高度。将它们除以 2,得到三角形的相邻边和相对边(分别为青色和绿色)。在上面的示例中,宽度 = 300 和高度 = 300。所以相邻边 = 150 和相反边 = 150。

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

          3. 找到θ。这是相邻边(青色)和斜边(红色)之间的角度。为此,您使用公式cos(theta) = (h^2 + a^2 - o^2)/2*h*o。应用此公式后,我们发现 theta = 0.785 (RADIANS)。要将其转换为度数,我们应用公式degrees = radians * 180 / PI = 45(度数)。这是斜边的初始(偏移)角度。意识到这一点非常重要。如果视图的视图旋转为零,则斜边的偏移角为 45(度)。我们很快就会需要 theta。

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

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

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

          -示例-

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

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

          另外,如果您想试用我创建的测试项目(就是图片中的那个),请随时从the following link 下载。

          更新

          这是一种精简的方法,可以计算您正在寻找的右上角偏移点(蓝色)。

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

          希望这会有所帮助!

          【讨论】:

          • 我总结了,但它不太管用:CGFloat相邻=rte.frame.size.width/2.0;CGFloat 相反=rte.frame.size.height/2.0;CGFloat hipotenuse=sqrtf(powf(相邻,2.0)+pow(对面,2.0));CGFloat theta=acosf((powf(hipotenuse,2.0)+powf(adjacent,2.0)-pow(opposite,2.0))/(2*hipotenuse*adjacent)); theta = theta*180/M_PI;CGFloat 弧度 = atan2f(rte.transform.b, rte.transform.a);CGFloat 度数 = 弧度 * (180 / M_PI);CGFloat 宽度 = cosf(degrees - theta) * hipotenuse;CGFloat height = sinf(degrees - theta) * hipotenuse;rightTopNode.center = CGPointMake(rte.center.x + width, rte.center.y + height);
          • 嗨@Vad。你没看错我的解释。您的代码中的错误是(这是因为我忘了解释那部分)因为 cosf() 和 sinf() 计算宽度和高度的参数必须以弧度为单位。另外,请参阅我的更新答案。我添加了一个精简的方法,它将为您计算点的坐标。最后,我不确定你为什么使用 atan2f。您提到您对视图使用旋转变换。旋转变换函数以弧度为单位进行旋转。您应该使用相同的角度来执行计算。希望这会有所帮助
          • 在某些时候我不能使用旋转变换,我想使用变换后的对象属性计算弧度 - 这就是为什么我有 atan2f(rte.transform.b, rte.transform.a);不是一样的吗?谢谢。
          • 嗨@Vad,很抱歉回复晚了。我刚刚使用atan2f 测试了该方法并且它有效。请记住在计算widthheight 时使用弧度。不要将theta 转换为弧度,直接使用atan2f 的结果,因为此函数返回弧度。
          • 不要使用这个——使用@LeoNatan 的@Bladebunny 等建议的CGPointApplyAffineTransform()CGRectApplyAffineTransform()
          【解决方案7】:

          你应该使用:

          CGPoint CGPointApplyAffineTransform (
             CGPoint point,
             CGAffineTransform t
          );
          

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

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2012-06-09
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多