Search code examples
iphoneioscore-animation

CGAffineTransformInvert does not reverse view transformation


I want to scale+translate a view and then animate this transformation back. But CGAffineTransformInvert returns a transformation that does something different (cannot understand the logic of it).

#import <UIKit/UIKit.h>
@interface TestView : UIView {
    UIView *view;
    CGAffineTransform transform;
}
@end


#import "TestView.h"
#import <QuartzCore/QuartzCore.h>
@implementation TestView
- (void)testAnimation:(NSString*)animationID finished:(NSNumber*)finished context:(void*)context {
    CGAffineTransform transformInverted = CGAffineTransformInvert(transform);
    [UIView beginAnimations:@"test2" context:NULL];
    [UIView setAnimationDuration:3.0];
    [UIView setAnimationCurve:UIViewAnimationCurveLinear];
    view.transform = transformInverted;
    [UIView commitAnimations];
}

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        // Initialization code
        view = [[UIView alloc] initWithFrame:CGRectMake(150, 150, 100, 100)];
        view.backgroundColor = [UIColor greenColor];
        [self addSubview:view];

        CGAffineTransform transform1 = CGAffineTransformTranslate(view.transform, -150, -150); 
        transform = CGAffineTransformScale(transform1, (float)1/2, (float)1/2); 

        [UIView beginAnimations:@"test1" context:NULL];
        [UIView setAnimationDuration:3.0];
        [UIView setAnimationCurve:UIViewAnimationCurveLinear];
        [UIView setAnimationDelegate:self];
        [UIView setAnimationDidStopSelector:@selector(testAnimation:finished:context:) ];

        view.transform = transform;

        [UIView commitAnimations];
    }
    return self;
}

Solution

  • CGAffineTransformInvert inverts the transform matrix, and returns a matrix that when multiplied by the original results in the CGAffineTransformIdentity.

    If you want to invert the translation/scale, you should track them yourself.

    PS. Matrices are very complex, they don't store the translation/scale/rotation values, they are composed of several values that when multiplied by the coordinates give you the translated/scaled/rotated values, so you can't expect to invert the matrix and get a matrix with the translation, scale and rotations inverted.

    You can read more about them here.

    EDIT TO ANSWER YOUR COMMENT:

    When working with matrices, the order of operations matters a lot, if you rotate and then translate, the result will be different than if you translate and then rotate.

    To reverse the operations applied to a matrix, you should apply the opposite operations in the reverse order, in this case, to reverse this:

    CGAffineTransform transform1 = CGAffineTransformTranslate(view.transform, x1, x2);
    CGAffineTransform transform2 = CGAffineTransformScale(transform1, r1, r2);
    CGAffineTransform transform3 = CGAffineTransformRotate(transform2, a);
    

    You'd need to do this:

    CGAffineTransform reversed3 = CGAffineTransformRotate(transform3, -a);
    CGAffineTransform reversed2 = CGAffineTransformScale(reversed3, 1/r1, 1/r2);
    CGAffineTransform reversed1 = CGAffineTransformTranslate(reversed2, -x1, -x2);
    
    [view setTransform:reversed1];
    

    If you hadn't modified the view's transform before, you can just do:

    [view setTransform:CGAffineTransformIdentity];
    

    Which is zero rotation, zero translation, and a scale of 1.