iOS下的2D仿射变换机制(CGAffineTransform相关)
仿射变换简介
仿射变换源于CoreGraphics框架,主要作用是绘制2D级别的图层,几乎所有iOS设备屏幕上的界面元素都是由CoreGraphics来负责绘制。而我们要了解的2D仿射变换是其下负责二维坐标到二维坐标的线性变换工作,它保持了二维图形的“平直性”(即:直线经过变换之后依然是直线,圆弧经过变换之后依然是圆弧)和“平行性”(即:二维图形之间的相对位置关系保持不变,平行线依然是平行线,且直线上点的位置顺序不变),只有依照向量产生的二维线条间的夹角会可能发生变化。仿射变换包括:平移(Translation)、缩放(Scale)、翻转(Flip)、旋转(Rotation)和错切(Shear)。iOS中通常只用到平移、旋转和缩放,同时iOS中CGAffineTransform是一个结构体级别的数据类型,并没有封装成类作为仿射变换,我们通常是在一个view的transform属性中进行访问和设置。常见于手势触发的旋转、平移、缩放,或者动画的类似播放效果。
CGAffineTransform简介
CGAffineTransform结构体代表了一种用于仿射变换矩阵。结构体的参数指定了从一个坐标系的点转化成另外一个坐标系的规则。仿射变换是一种特殊类型的映射,保留在一个路径中的平行线,但不一定保留长度或角度。缩放,旋转,平移是最常用的操作支持的仿射变换,但也可能扭曲。我们通常不会直接创建一个仿射变换,我们只需要根据现有的参数,修改现有的仿射变换。
我们创建一个view,当给它的transform属性赋值的时候,就是我们设置仿射变换的值,我们具体来分析一下仿射变换的具体细节。
UIView *view = [[UIView alloc]initWithFrame:CGRectMake(100, 100, 100, 100)]; view.backgroundColor = [UIColor cyanColor]; //视图的仿射变换设定:旋转 view.transform = CGAffineTransformMakeRotation(M_1_PI); [self.view addSubview:view];
仿射变换机制
仿射变换是通过下面的矩阵来完成的,一共有9个参数,我们通过这9个参数来进行仿射变换。但是矩阵的最后一列总是0,0,1三个固定的值,所以实际上创建一个变换的结构体只需要6个参数,也就是下面给出的a,b,c,d,tx,ty六个CGFloat类型的参数。
仿射变换是通过下面的矩阵来完成的,一共有9个参数,我们通过这9个参数来进行仿射变换。但是矩阵的最后一列总是0,0,1三个固定的值,所以实际上创建一个变换的结构体只需要6个参数,也就是下面给出的a,b,c,d,tx,ty六个CGFloat类型的参数(通过结构体来描述的六个值)。
CGAffineTransform t = CGAffineTransformMake(CGFloat a, CGFloat b, CGFloat c, CGFloat d, CGFloat tx, CGFloat ty);
变化原理:源视图上的点p(x,y)变化成目标视图中p'(x',y')。对应关系为:
这样源视图上的每一点都这样变换成目标视图的点的过程,就叫做仿射变换。
平移变换:
那么依照上面的矩阵,我们如果想要达到平移的目的,只需要x' = x + tx,y' = y + ty(改变对应点的横纵坐标的位置即可)。那么这样算来,仿射变换的参数中,a = 1,b = 0, c = 0, d = 1。也就是说,我们确定了四个参数,那么我们可以根据自己的需求给定tx,ty,这样就完成了平移的设置,也就是说,平移的仿射变换参数只有tx,ty是不确定的,所以系统的平移只提供了两个参数的原因(内部帮我们补充了固定的参数)。
CGAffineTransformMakeTranslation(CGFloat tx, CGFloat ty)
当然细心的朋友发现还有另外一个平移变换的构造:
CGAffineTransformTranslate(CGAffineTransform t, CGFloat tx, CGFloat ty)
两者的区别当我们对一个视图进行平移操作的时候,前者每次都会从初始位置开始平移(操作前回到原来的位置),后者是从给定参数(通常是上一次的状态)的基础上继续平移。
缩放变换:
缩放的操作,本质上是拉长或缩短了原来的点与点之间的距离,那么也就是说我们只需要让x' = a * x,y' = d *y(将一个点的横坐标和纵坐标放大或者缩小若干倍)。根据平移的经验,那么这样算来,仿射变换的参数中,b = 0, c = 0,tx = 0,ty = 0。也就是说,我们确定了四个参数,那么我们可以根据自己的需求给定a,b,这样就完成了缩放的设置,系统称变换的系数为sx,sy。同样它有两种给定参数的方式:
CGAffineTransformMakeScale(CGFloat sx, CGFloat sy)CGAffineTransformScale(CGAffineTransform t, CGFloat sx, CGFloat sy)
前者依然是在视图初始状态的基础上进行缩放(每次缩放前都先回到初始状态),后者是在前一次操作后的基础上进行缩放操作。
旋转变换:
在iOS中,视图的旋转是按照顺时针进行一个角度(假定为α)的改变,按照数学理解,我们要想让一个点通过旋转到另一个点(不熟悉的朋友复习一下向量的知识),那我们的变化过程应该是这样x' = x * cos α - y *sin α,y’ = x *sin α + y *cos α。所以旋转操作中,tx、ty等于0,a = cosα,b = sinα, c = sinα,d = cosα。虽然有四个参数,但是全部依赖于我们给定的角度,所以系统帮我们封装了起来(角度参数为angle),我们只需要设定一个角度就可以了。所以系统通过的构造旋转操作的参数是:
CGAffineTransformMakeRotation(CGFloat angle)CGAffineTransformRotate(CGAffineTransform t, CGFloat angle)
同样,前者是在初始化的基础上操作旋转(每次旋转先回到初始状态),而后者是在上一个参数(通常情况下是上一次的操作结果)的基础上旋转。
iOS中的2D仿射变换常用的就是以上三种,通常配合手势或者动画来操作。通常iOS中视图的transform支持的就是这三种变换,我们只需要按照自己的需要进行创建就可以,依照系统给定的参数,我们填入合适的值之后,系统会转化成2D仿射对应矩阵,当然如果不满意系统提供的仿射变换,可以自己定义仿射的实现。