如何理解CGAffineTransform

December 17, 2023
测试
测试
测试
测试
5 分钟阅读
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];

大功告成。

继续阅读

更多来自我们博客的帖子

如何安装 BuddyPress
由 测试 December 17, 2023
经过差不多一年的开发,BuddyPress 这个基于 WordPress Mu 的 SNS 插件正式版终于发布了。BuddyPress...
阅读更多
Filter如何工作
由 测试 December 17, 2023
在 web.xml...
阅读更多
如何学习Python
由 测试 December 17, 2023
Python是什么 Python(英语发音:/ˈpaɪθən/), 是一种面向对象、解释型计算机程序设计语言,由Guido van...
阅读更多