CGAffineTransform二维视图旋转、缩放、平移变换详解

本文详细介绍了CoreGraphics框架下的CGAffineTransform结构体,包括平移、缩放、旋转和组合变换的实现原理,并通过实例演示了如何使用CGAffineTransform进行视图变换。

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

1. 平移

平移初始化方法如下

/* 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轴向下为正向)

2. 缩放

缩放初始化方法如下

/* 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进行缩放,传入负值可进行水平或垂直镜像

3. 旋转

旋转初始化方法如下

/* 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'

Demo

我们用一个ImageView来演示一下

1. 平移
jfImageView.transform = CGAffineTransform(translationX: 50, y: -50)
复制代码
2. 缩放
jfImageView.transform = CGAffineTransform(scaleX: 2, y: 2)
复制代码
3. 旋转
jfImageView.transform = CGAffineTransform(rotationAngle: -.pi/4)
复制代码
4. 组合变换(重点展开说明)
  • 先逆时针旋转45度,再放大两倍。
jfImageView.transform = CGAffineTransform(rotationAngle: -.pi/4).scaledBy(x: 2, y: 2)
复制代码
  • 先逆时针旋转45度,再放大两倍,再沿x轴正向移动50,y轴负向移动50。
jfImageView.transform = CGAffineTransform(rotationAngle: -.pi/4).scaledBy(x: 2, y: 2).translatedBy(x: 50, y: -50)
复制代码
  • 先逆时针旋转45度,再沿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》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值