仿射变换(一)- UIView的平移,旋转,放缩


1、需求来源:

       现在需要实现一个图片编辑功能,能够对一个图片进行平移,旋转,放缩。因为考虑到图片操作的流畅度,性能问题。这个功能分成两个部分完成:

  1. 把缩略图放入UIImageView,对UIImageView进行平移,旋转,放缩,把这些数据记录为一个3*3矩阵(仿射矩阵,具体细节这里不阐述)
  2. 通过画板,把仿射矩阵+原图片相结合,画出在原图上的处理图片。

2、简单的UIImageView手势识别,图片处理。

2.1 图片放缩

- (void)pinchGesture:(UIPinchGestureRecognizer *)recognizer{
    if ([recognizer state] == UIGestureRecognizerStateBegan){
       _lastScale = 1.0;
    }
    else if([recognizer state] == UIGestureRecognizerStateChanged) {
        CGFloat scale = 1.0 - (_lastScale - [recognizer scale]);
        self.imageView.transform = CGAffineTransformScale([self.imageView transform], scale, scale);
       _lastScale = [recognizer scale];
    }else{  
    //对缩放比例进行控制
    }
}   
  • CGFloat scale = 1.0 - (_lastScale - [recognizer scale]); scale没有重置的情况下,每次的[recognizer scale]是相对于原始图片的scale,此处我们应该是在上次放缩结果的基础上,继续放缩。

  • CGAffineTransformScale([self.imageView transform], scale, scale); 在上次的Transform的基础上,做了一次Scale的操作。

    2.2 图片旋转

    <pre>- (void)rotationGesture:(UIRotationGestureRecognizer *)recognizer{ if ([recognizer state] == UIGestureRecognizerStateBegan || [recognizer state] == UIGestureRecognizerStateChanged) { _imageView.transform = CGAffineTransformRotate([_imageView transform], [recognizer rotation]); [recognizer setRotation:0]; }else{ CGAffineTransform _trans = _imageView.transform; } }</pre>

  • [recognizer setRotation:0];此处便是对recognizer进行了重置。和 2.1 里面相对应。同理 2.1 也可以使用 [recognizer setScale:0]进行设置

  • _imageView.transform = CGAffineTransformRotate([_imageView transform], [recognizer rotation]);此处类似scale,在上一次的Transform的基础上,进行旋转

       至此,图片的放缩和旋转数据,便会存储在 imageView的transfrom里面,我们可以通过 CGAffineTransform _trans = _imageView.transform; ** 3 * 3 矩阵。**

       下面,我们会处理平移,这个看似最简单的功能,但其实,是最繁琐的功能。

2.3 图片移动

方案1
CGPoint translation = [recognizer translationInView:recognizer.view];
CGFloat tranX = translation.x;
CGFloat tranY = translation.y;
_imageView.center = CGPointMake(_imageView.center.x + tranX, _imageView.center.y + tranY);

       这个方法可以很简单的实现图片的上下左右移动,看起来似乎很容易,但别忘了我们的初衷,我们希望用** 仿射矩阵 来存储 移动数据 ,但我们现在的方案,仅仅是改变UIView的center而促使了图片的移动,和 仿射矩阵 **没有半毛钱的关系。

       那我们该怎么把数据设置到 仿射矩阵

方案2
self.imageView.transform = CGAffineTransformTranslate(self.imageView.transform, x, y);

       第二种方案,用Transform实现图片的移动。初看的时候,移动似乎很完美,没有任何问题,数据刚好也自动写入Transfrom的矩阵,很完美!!!!!

       但仔细测试,会发现,如果旋转和缩放之后,这里的移动就显得很奇怪。。为什么呢?

       因为,Transform 会改变图片的坐标系,比如,图片顺时针旋转了45°,那么坐标系便整体顺时针旋转45°,所以当你再次运用上面代码的时候,期望的图片往右平移,但实际的结果图片往右下角平移。同理,如果图片放大两倍,你就会发现同样的平移,图片移动的更快(因为,坐标系同比例放大了两倍!)。

解决方案

那现在问题来了,该如何解决?初略想一下,两个解决方案

  • 方案1 情况进行平移,然后研究 3 * 3 仿射矩阵,把数据镶嵌到仿射矩阵里面。

  • 方案2 情况,先把recognizer获取的x,y,(此处的x,y是相对于屏幕的),然后按照当前图片坐标系,进行转换,最后按 CGAffineTransformTranslate 进行移动,自动集成到 仿射矩阵里。

    我在这里用的第二种解决方案,

    如图1

图1.png

直接上代码:

   CGAffineTransform _trans = _imageView.transform;
   currentDegree = atan2(_trans.b, _trans.a);
   currentScale = sqrt(pow(_trans.a, 2) + pow(_trans.c, 2));

   //三角函数坐标转换
   CGFloat x;
   CGFloat y;
   if(currentDegree < 0){
       x = translation.x * cos(-currentDegree) - translation.y * sin(-currentDegree);
       y = translation.x * sin(-currentDegree) + translation.y * cos(-currentDegree);
   }else{
       x = translation.x * cos(currentDegree) + translation.y *sin(currentDegree);
       y = - translation.x * sin(currentDegree) + translation.y * cos(currentDegree);
   }
   self.imageView.transform = CGAffineTransformTranslate(self.imageView.transform, x, y);
  • currentDegree = atan2(_trans.b, _trans.a); 能获取当前旋转角度
  • currentScale = sqrt(pow(_trans.a, 2) + pow(_trans.c, 2)); 能获取当前的放缩比例
  • 下面紧接着做 水平X,Y 到图片当前坐标系的转换(此处我没有考虑 scale 的情况,仅仅考虑旋转的坐标转换,scale相对比较简单),高中知识忘得差不多了,做的时候还脑补了一下。
  • 然后用CGAffineTransformTranslate应用到图片上

总结

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,884评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,755评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,369评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,799评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,910评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,096评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,159评论 3 411
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,917评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,360评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,673评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,814评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,509评论 4 334
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,156评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,882评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,123评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,641评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,728评论 2 351

推荐阅读更多精彩内容