iOS 视图的二维变换

简介

UIView 对象有一个 transform 属性,该属性可以使控件产生移动、缩放、旋转效果,其坐标系统采用的是二维坐标系,坐标原点为屏幕的左上角,向右为 x 轴正方向,向下为 y 轴正方向。

几种效果

  • 移动

CGAffineTransformMakeTranslation(CGFloat tx, CGFloat ty) 以初始位置为基准,在 x 轴方向上平移 x 单位,在 y 轴方向上平移y单位。

// 将图片左(100px)下(150px)方向移动
CGAffineTransform transform = CGAffineTransformMakeTranslation(-100, 150);
self.imageView.transform = transform;

可以结合视图动画进行变换,这样可以看到变换的过程。

CGAffineTransformTranslate(CGAffineTransform t, CGFloat tx, CGFloat ty) 在已有的 transform 基础上,增加 移动 效果。

self.imageView.transform = CGAffineTransformTranslate(self.imageView.transform, -50, 150); 
  • 缩放
// 以初始位置为基准,在x轴方向上缩放x倍,在y轴方向上缩放y倍
CGAffineTransformMakeScale(CGFloat sx, CGFloat sy)
// 在已有的 transform 基础上,增加缩放效果
CGAffineTransformScale(CGAffineTransform t, CGFloat sx, CGFloat sy)
  • 旋转
// 以初始位置为基准,将坐标系统旋转angle弧度(弧度=π/180×角度,M_PI弧度代表180角度)
CGAffineTransformMakeRotation(CGFloat angle)
// 在已有的transform基础上,增加旋转效果
CGAffineTransformRotate(CGAffineTransform t, CGFloat angle)
  • 初始 transform
    控件的 transform 属性默认值为CGAffineTransformIdentity,通过设置该值可以将控件还原到最初的状态。
self.imageView.transform = CGAffineTransformIdentity;
  • 反转变换效果

CGAffineTransformInvert(CGAffineTransform t) 接受一个 transform ,可以实现和参数相反的效果,比如放大3倍则会缩小为1/3,向x轴正方向平移100px效果则为向负方向平移100px。

CGAffineTransform transform = CGAffineTransformMakeScale(3, 3);  
// 缩小为1/3      
transform = CGAffineTransformInvert(transform);
self.imageView.transform = transform;
  • 组合变换效果
    CGAffineTransformConcat(CGAffineTransform t,CGAffineTransform t) 可以组合两种变换效果。
CGAffineTransform transform_A = CGAffineTransformMakeTranslation(0, 200);
CGAffineTransform transform_B = CGAffineTransformMakeScale(0.2, 0.2);
transform = CGAffineTransformConcat(transform_B, transform_A);

操作 transform

  • 比较等同性

变换是可以比较的,我们可以判断两个变换是否相同。

// 判断变换是否是最初的状态
bool CGAffineTransformIsIdentity(CGAffineTransform t)
// 判断两个变换是否相同
bool CGAffineTransformEqualToTransform(CGAffineTransform t1, CGAffineTransform t2) 
  • 转换 point、size、rect

在进行变换之后,视图的 point、size、rect 都可能发生变化,我们可以通过特定的方法得到变换后的 point、size、rect。

// 得到转换后的point
CGPoint CGPointApplyAffineTransform(CGPoint point, CGAffineTransform t)
// 得到转换后的size
CGSize CGSizeApplyAffineTransform(CGSize size, CGAffineTransform t)
// 得到转换后的rect
CGRect CGRectApplyAffineTransform(CGRect rect, CGAffineTransform t)

CGAffineTransform原理

CGAffineTransform 形变是通过 仿射变换矩阵 控制的,其中平移是矩阵相加,旋转与缩放则是矩阵相乘,CGAffineTransform 形变就是把二维形变使用一个三维矩阵来表示,系统提供了 CGAffineTransformMake 结构体来控制形变。

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

该三维变换矩阵如下:


变换矩阵

变换矩阵

通过变换矩阵左乘向量,将空间中的一个点集从一个坐标系变换到另一个坐标系中,计算方式如下:

计算

矩阵相乘


这里写图片描述

计算结果

由此可知,
tx:用来控制在x轴方向上的平移
ty:用来控制在y轴方向上的平移
a:用来控制在x轴方向上的缩放
d:用来控制在y轴方向上的缩放
abcd:共同控制旋转

所以以下写法都是等同的。

  • 移动:[ 1 0 0 1 tx ty ]
CGAffineTransformMakeTranslation(100, 100);
CGAffineTransformMake(1, 0, 0, 1, 100, 100);
  • 缩放:[ sx 0 0 sy 0 0 ]
CGAffineTransformMakeScale(2, 0.5);
CGAffineTransformMake(2, 0, 0, 0.5, 0, 0);
  • 旋转:[ cos(angle) sin(angle) -sin(angle) cos(angle) 0 0 ]
CGAffineTransformMakeRotation(M_PI*0.5);
CGAffineTransformMake(cos(M_PI * 0.5), sin(M_PI * 0.5), -sin(M_PI * 0.5), cos(M_PI * 0.5), 0, 0);
  • 最初:[ 1 0 0 1 0 0 ]
CGAffineTransformIdentity;
CGAffineTransformMake(1, 0, 0, 1, 0, 0);

简单应用

  • 结合UIView动画使用
[UIView animateWithDuration:1.0 animations:^{
    //缩放
    CGAffineTransform transform = CGAffineTransformMakeScale(2, 2);           
    self.imageView.transform = transform;
} completion:^(BOOL finished) {
    [UIView animateWithDuration:1.0 animations:^{
        //回到最初
        self.imageView.transform =  CGAffineTransformIdentity;              
    } completion:nil]; 
}];
  • 结合手势使用
// 点击手势
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapAction:)];
tap.numberOfTapsRequired = 2;
[self.testView addGestureRecognizer:tap];
//拖拽手势
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panAction:)];
[self.testView addGestureRecognizer:pan];


-(void)tapAction:(UITapGestureRecognizer *)tap{
    if (CGAffineTransformIsIdentity(self.testView.transform)) {
        [UIView animateWithDuration:0.5 animations:^{
            self.testView.transform = CGAffineTransformScale(self.testView.transform, 1.3, 2);
        }];
    }
    else{
        [UIView animateWithDuration:0.5 animations:^{
            self.testView.transform = CGAffineTransformIdentity;
        }];
    }
}


-(void)panAction:(UIPanGestureRecognizer *)pan{
    //获取手势位置
    CGPoint position = [pan translationInView:self.testView];
    //通过 CGAffineTransformTranslate 获取 新的transform
    self.testView.transform = CGAffineTransformTranslate(self.testView.transform, position.x, position.y);
    //将增加置为 0
    [pan setTranslation:CGPointZero inView:self.testView];
}

总结

变换属性可以控制视图进行移动、缩放、移动,结合视图动画或者手势可以实现一些简单的动画效果。变换会真实的改变动画的 frame、size、rect,可以通过特定的方法变换这些值。CGAffineTransform 形变是通过 仿射变换矩阵 控制的,其中平移是矩阵相加,旋转与缩放则是矩阵相乘。

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

推荐阅读更多精彩内容