Trouver les coordonnées du Cadre après L'application de la transformation UIView (Gcaffinetransform))

je tourne ma vue avec CGAffineTransform

[view setTransform:newTransform];

les valeurs du cadre restent les mêmes après l'application de transform, mais comment puis-je trouver les valeurs "tournées" ou transformées de ce cadre?

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

je veux les coordonnées exactes des bords tournés du cadre, c'est-à-dire les points a, b, C, D.

26
demandé sur Vad 2013-10-22 20:33:20

6 réponses

une chose à garder à l'esprit est que la transformation change le système de coordonnées, vous aurez donc besoin d'être en mesure de convertir entre la 'vue' du parent et la vue de l'enfant (transformé). Aussi, transforme préserver le centre de l'objet transformé, mais les autres coordonnées. Donc vous devez calculer les choses en termes de Centre. Et il y a plusieurs aides dont vous aurez besoin. (J'ai obtenu la plupart de l'approche suivante du Livre de base iOS de Erica Sadun Cookbook).

je les ajoute habituellement comme une catégorie sur uivi.

pour transformer les coordonnées de l'enfant en celles du parent, vous avez besoin de quelque chose comme:

// 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)
    }
}
43
répondu Bladebunny 2017-12-11 00:06:22

vous pouvez trouver les coordonnées de la vue tournée en utilisant la trigonométrie de base. Voici comment vous pouvez le faire:

enter image description here

  1. la première étape consiste à connaître la largeur et la hauteur de votre vue. Divisez - les par 2 et vous obtenez les côtés adjacents et opposés de votre triangle (cyan et vert respectivement). Dans l'exemple ci-dessus, largeur = 300 et hauteur = 300. Donc adjacentSide = 150 et oppositeSice = 150.

  2. Trouver l'hypoténuse (rouge). Pour cela, vous utilisez la formule: h^2 = a^2 + b^2 . Après application de cette formule nous trouvons l'hypoténuse = 212.13

  3. trouver theta. C'est l'angle entre la adjacentSide (cyan) et l'hypoténuse (rouge). Pour cela, vous utilisez la formule cos(theta) = (h^2 + a^2 - o^2)/2*h*o . Après application de cette formule nous trouvons que theta = 0.785 (RADIANS). Pour convertir cela en degrés nous appliquons la formule degrees = radians * 180 / PI = 45 (degrés). C'est l'angle initial (offset) de l'hypoténuse. Ceci est très important à comprendre. SI LA ROTATION DE LA VUE DE VOTRE VUE EST ZÉRO L'HYPOTÉNUSE A UN ANGLE DE DÉPORT DE 45 (DEGRÉS). Nous allons bientôt avoir besoin de thêta.

  4. maintenant que nous connaissons l'hypoténuse (rouge) nous avons besoin de l'anneau de rotation. Dans cet exemple, j'ai utilisé un UIRotationGestureRecognizer pour tourner la vue carrée. Cette classe a une propriété "rotation" qui nous indique combien le point de vue a tourné. Cette valeur est en RADIANS. Dans l'exemple ci-dessus, la rotation est de 0,3597 RADIANS. Pour le convertir en degrés, nous utilisons la formule degrees = radians * PI / 180 . Après avoir appliqué la formule, nous trouvons que l'angle de rotation est de 20,61 degrés.

  5. nous pouvons enfin trouver la largeur offset (orange) et la hauteur (pourpre). Pour la largeur, nous utilisons la formule width = cos(rotationAngle - theta) * hypotenuse et pour la taille, nous utilisons la formule height = sen(rotationAngle - theta) . NOUS DEVONS SOUSTRAIRE THÊTA (EN RADIANS!) À PARTIR DE LA ANGLE DE ROTATION (EN RADIANS AUSSI!) PARCE QUE THETA ÉTAIT L'OFFSET INITIAL. Vue de cette façon: l'hypoténuse a un angle de 45 degrés) lorsque l'angle de rotation était de zéro. Après avoir appliqué les formules, nous trouvons que la largeur = 193,20 et la hauteur = -87,60

  6. enfin, nous pouvons ajouter ces valeurs (largeur et hauteur) au centre du carré pour trouver les coordonnées du point bleu.

- exemple -

// Get the center point
CGPoint squareCenter = self.squareView.center;

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

les coordonnées des points bleus sont (963.20, 272.40)

pour mieux comprendre les formules s'il vous plaît voir les liens suivants:

aussi, si vous voulez jouer avec le projet test que j'ai créé (c'est celui de l'image) s'il vous plaît n'hésitez pas à télécharger à partir de le lien suivant .

mise à JOUR

Voici une méthode condensée qui va calculer le décalage du point en haut à droite (bleu) que vous recherchez.

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

Espérons que cette aide!

16
répondu LuisCien 2013-10-30 07:11:26

vous devez utiliser:

CGPoint CGPointApplyAffineTransform (
   CGPoint point,
   CGAffineTransform t
);

pour obtenir un point spécifique, utilisez les limites et le centre de la vue, puis appliquez la transformation de la vue pour obtenir un nouveau point après transformation. C'est mieux que d'ajouter du code spécifiquement pour la transformation de rotation, car il peut supporter n'importe quelle transformation ainsi que l'enchaînement.

4
répondu Leo Natan 2013-10-27 00:45:47

essayez ce code

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);
1
répondu MarekR 2013-10-31 16:39:28

pourquoi tout ce bazar? Garder ça simple? Là où x était avant la transformation, ce sera q rads/degrés plus loin comme tous les autres points autour de l'ancre.

allait tout expliquer, mais ce chap dans ce post l'a expliqué dans un contexte encore plus court:

obtenir l'angle de courant / rotation / radian pour un uivi?

CGFloat radians = atan2f(yourView.transform.b, yourView.transform.a); 
CGFloat degrees = radians * (180 / M_PI);
1
répondu Adrian Sluyters 2017-05-23 12:18:04

j'ai écrit cette classe qui peut nous aider:

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

par exemple, je l'utilise pour restreindre le mouvement/échelle / rotation d'un autocollant à l'intérieur d'un conteneur vue de la manière suivante:

@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;
}
0
répondu kanobius 2016-07-15 16:36:00