PRML 2017-12-16
CoreGraphics框架下的CGAffineTransform结构体,可以实现对视图或图层的旋转、缩放、平移及组合变换。
public struct CGAffineTransform { public var a: CGFloat public var b: CGFloat public var c: CGFloat public var d: CGFloat public var tx: CGFloat public var ty: CGFloat public init() public init(a: CGFloat, b: CGFloat, c: CGFloat, d: CGFloat, tx: CGFloat, ty: CGFloat) }
可以看到CGAffineTransform是一个结构体,它会被组装成如下矩阵
视图原坐标(x,y)经过如下变换,转换为(x',y')将该矩阵展开后即得到如下等式,仿射变换完全按照如下关系进行x' = ax+cy+tx
y' = bx+dy+ty
平移初始化方法如下
/* Return a transform which translates by `(tx, ty)': t' = [ 1 0 0 1 tx ty ] */ public init(translationX tx: CGFloat, y ty: CGFloat)
经过坐标变换,可以得到x' = x+tx
y' = y+ty
平移看起来非常简单,tx为正值则向x轴正向平移,反之向负向平移,ty值同理(x轴向右为正向,y轴向下为正向)
缩放初始化方法如下
/* Return a transform which scales by `(sx, sy)': t' = [ sx 0 0 sy 0 0 ] */ public init(scaleX sx: CGFloat, y sy: CGFloat)
经过坐标变换,可以得到x' = x*sx
y' = y*sy
根据传入sx, sy进行缩放,传入负值可进行水平或垂直镜像
旋转初始化方法如下
/* Return a transform which rotates by `angle' radians: t' = [ cos(angle) sin(angle) -sin(angle) cos(angle) 0 0 ] */ public init(rotationAngle angle: CGFloat)
这里入参是一个弧度值,角度x转换为弧度angle = x*Double.pi/180
旋转是绕着视图中心点进行,angle为正值则视图绕着中心点顺时针旋转
下面推导一下这个变换矩阵
假设v点的坐标是(x,y),那么可以推导得到 v' 点的坐标(x’,y’)(设原点到v的距离是r,原点到v点的向量与x轴的夹角是ϕ )x=rcosϕ, y=rsinϕ
x′=rcos(θ+ϕ), y′=rsin(θ+ϕ)
通过三角函数展开得到
x′=rcosθcosϕ−rsinθsinϕ
y′=rsinθcosϕ+rcosθsinϕ
带入x和y表达式得到
x′=xcosθ−ysinθ
y′=xsinθ+ycosθ
写成矩阵的形式即:
该变换矩阵即初始化中的t'我们用一个ImageView来演示一下
jfImageView.transform = CGAffineTransform(translationX: 50, y: -50)
jfImageView.transform = CGAffineTransform(scaleX: 2, y: 2)
jfImageView.transform = CGAffineTransform(rotationAngle: -.pi/4)
jfImageView.transform = CGAffineTransform(rotationAngle: -.pi/4).scaledBy(x: 2, y: 2)
jfImageView.transform = CGAffineTransform(rotationAngle: -.pi/4).scaledBy(x: 2, y: 2).translatedBy(x: 50, y: -50)
jfImageView.transform = CGAffineTransform(rotationAngle: -.pi/4).translatedBy(x: 50, y: -50).scaledBy(x: 2, y: 2)
后面两种组合变换得到的结果是不一样的先来看三种变换后的视图frame(4英寸屏幕大小下)
当对图层做变换的时候,比如旋转或者缩放,frame实际上代表了覆盖在图层旋转之后的整个轴对齐的矩形区域,也就是说frame的宽高可能和bounds的宽高不再一致了,就如我们的图片一样,frame = {6.56, 130.56, 306.88, 306.88},bounds = {0, 0, 128, 89}
变换2相对1竖直向上移动了141.42,变换3相对1竖直向上移动了70.71,两个变换的x值均未变化。原因在于当按顺序做了变换,上一个变换的结果将会影响之后的变换。组合变换2的translatedBy(x: 50, y: -50)被逆时针旋转了45度,放大了两倍,因此竖直向上移动了100√2≈141.42 组合变换3的translatedBy(x: 50, y: -50)只是被逆时针旋转了45度,因此竖直向上移动了50√2≈70.71
Demo下载
参考资料:
Documentation>Core Graphics>CGAffineTransform
旋转变换(一)旋转矩阵
《iOS Core Animation》