如何理解CGAffineTransform

CGAffineTransform

A structure for holding an affine transformation matrix.

以上是它的定义,其实就是一个矩阵的结构体,经常用于动画,形状变换。

包含如下参数:

struct CGAffineTransform { CGFloat a; CGFloat b; CGFloat c; CGFloat d; CGFloat tx; CGFloat ty; }; typedef struct CGAffineTransform CGAffineTransform;  

下面直观的描述这个这个矩阵和坐标之间的关系。

一个实验

  • 给一个UIImageView添加手势
    //zoom手势
    UIPinchGestureRecognizer* zoomer = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(editImageWithZoom:)];
    
    UIRotationGestureRecognizer* rotation = [[UIRotationGestureRecognizer alloc] initWithTarget:self action:@selector(editImageWithRotation:)];
    [imageView addGestureRecognizer:zoomer];
    
    [imageView addGestureRecognizer:rotation];
  
  • 手势实现方法
//缩放
-(void)editImageWithZoom:(UIPinchGestureRecognizer*)sender
{
    CGAffineTransform transform= CGAffineTransformScale(originTransform, sender.scale, sender.scale);
    imageView.transform=transform;
}
//旋转
-(void)editImageWithRotation:(UIRotationGestureRecognizer*)sender
{
    CGAffineTransform transfrom = CGAffineTransformRotate(originTransform, sender.rotation);
    imageView.transform=transfrom;
}

其中的两个方法CGAffineTransformScaleCGAffineTransformRotate是生成旋转和缩放的矩阵,当然也可以直接使用通用方法

CGAffineTransform CGAffineTransformMake ( CGFloat a, CGFloat b, CGFloat c, CGFloat d, CGFloat tx, CGFloat ty );

生成对应的矩阵。

  • 继续变换

不修改任何代码,继续缩放和旋转。会发现每次都重新归位后旋转。

原来是CGAffineTransformIdentity这个常量搞的鬼。

每一次的rotatescale都是在这个常量的基础上变换的。



这个是它的定义。

解决这个问题只要在手势代码中加入

    if(sender.state==UIGestureRecognizerStateEnded || sender.state==UIGestureRecognizerStateCancelled)
    {
        //结束手势
        originTransform=imageView.transform;
    }

其中的originTransform可以定义为成员变量,初始化代码。

originTransform = CGAffineTransformIdentity;
  • 坐标变换之后出现的问题

    意识到CGAffineTransform所做的变换其实是对坐标系做的变换。因此变换完以后使用平移操作会发现坐标系变换以后产生的影响。解决方案:

  • 取父view的坐标系,更改imageView.center,因为不论是scale还是rotationcenter的点是不变的。

获取变换后的参数

变换以后需要取得变换以后的scalerotation

打变量观察。

(lldb) po transistion
 (a = 0.69003591274966281, b = -1.6204680103221447, c = 1.6204680103221447, d = 0.69003591274966281, tx = 0, ty = 0)  

其中scale是(双指缩放sx=sy):



rotation是:



联合作用在单位对角矩阵上:可以得到最终的transfrom:



可以解得:

好吧根本解不出来。另寻他路。

打算用成员变量接受每一次旋转和缩放后的参数。

打出每一次旋转和缩放操作的scalerotation。发现每一次都是重新从1和0开始计算。

于是简单了,在每一次手势结束的时候加上原来的参数。

-(void)editImageWithRotation:(UIRotationGestureRecognizer*)sender
{

    CGAffineTransform transfrom = CGAffineTransformRotate(originTransform, sender.rotation);
    imageView.transform=transfrom;
   // NSLog(@"%lf",sender.rotation);
    if(sender.state==UIGestureRecognizerStateEnded || sender.state==UIGestureRecognizerStateCancelled)
    {
        //结束手势
        radians = radians+sender.rotation;
        originTransform=imageView.transform;
    }
}

scale类似方法获得。

输出最后imageViewframe和最开始的frame

frame = (247.357 307.2; 273.285 409.6)  //最初的
frame = (142.016 271.144; 483.968 481.711)  //变换后的
r = 0.79710480433663233  //旋转参数

swift的牛逼的playground下调试

let r = 0.79710480433663233
let w = 273.285
let h = 409.6
let nw = h*cos(r)+w*sin(r)
let nh = h*sin(r)+w*cos(r)

发现rect旋转后的rect其实是这样:



所以要获取用户变换以后的图片,可以这么来。

    UIImage* editedImge = [image imageByScalingToSize:CGSizeMake(originRect.size.width*scale, originRect.size.height*scale)];
    editedImge = [editedImge imageRotatedByRadians:rotation];
    
    //获取最终点的坐标
    [editedImge drawInRect:rect];

大功告成。