方法介绍
CGAffineTransformMakeTranslation实现以初始位置为基准,在x轴方向上平移x单位,在y轴方向上平移y单位
// 格式
CGAffineTransformMakeTranslation(CGFloat tx, CGFloat ty) // 样例 self.demoImageView.transform = CGAffineTransformMakeTranslation(100, 100);
注: 当tx为正值时,会向x轴正方向平移,反之,则向x轴负方向平移;当ty为正值时,会向y轴正方向平移,反之,则向y轴负方向平移
- CGAffineTransformMakeScale实现以初始位置为基准,在x轴方向上缩放x倍,在y轴方向上缩放y倍
// 格式 CGAffineTransformMakeScale(CGFloat sx, CGFloat sy) // 样例 self.demoImageView.transform = CGAffineTransformMakeScale(2, 0.5);
注: 当sx为正值时,会在x轴方向上缩放x倍,反之,则在缩放的基础上沿着竖直线翻转;当sy为正值时,会在y轴方向上缩放y倍,反之,则在缩放的基础上沿着水平线翻转
- CGAffineTransformMakeRotation实现以初始位置为基准,将坐标系统
逆
时针旋转angle弧度(弧度=π/180×角度,M_PI弧度代表180角度)// 格式 CGAffineTransformMakeRotation(CGFloat angle) // 样例 self.demoImageView.transform = CGAffineTransformMakeRotation(M_PI*0.5);
注1: 当angle为正值时,
逆
时针旋转坐标系统,反之顺
时针旋转坐标系统注2:
逆
时针旋转坐标系统的表现形式为对控件进行顺
时针旋转
- CGAffineTransformTranslate实现以一个已经存在的形变为基准,在x轴方向上平移x单位,在y轴方向上平移y单位
// 格式 CGAffineTransformTranslate(CGAffineTransform t, CGFloat tx, CGFloat ty) // 样例 CGAffineTransform transform = CGAffineTransformMakeTranslation(50, 50); self.demoImageView.transform = CGAffineTransformTranslate(transform, 50, 50);
- CGAffineTransformScale实现以一个已经存在的形变为基准,在x轴方向上缩放x倍,在y轴方向上缩放y倍
// 格式 CGAffineTransformScale(CGAffineTransform t, CGFloat sx, CGFloat sy) // 样例 CGAffineTransform transform = CGAffineTransformMakeScale(2, 0.5); self.demoImageView.transform = CGAffineTransformScale(transform, 2, 1);
- CGAffineTransformRotate实现以一个已经存在的形变为基准,将坐标系统
逆
时针旋转angle弧度(弧度=π/180×角度,M_PI弧度代表180角度)// 格式 CGAffineTransformRotate(CGAffineTransform t, CGFloat angle) // 样例 CGAffineTransform transform = CGAffineTransformMakeRotation(M_PI*0.25); self.demoImageView.transform = CGAffineTransformRotate(transform, M_PI*0.25);
- 特殊地,transform属性默认值为CGAffineTransformIdentity,可以在形变之后设置该值以还原到最初状态
// 样例 self.demoImageView.transform = CGAffineTransformIdentity;
CGAffineTransform原理
CGAffineTransform形变是通过"仿射变换矩阵"来控制的,其中平移是矩阵相加,旋转与缩放则是矩阵相乘,为了合并矩阵运算中的加法和乘法,引入了齐次坐标的概念,它提供了用矩阵运算把二维、三维甚至高维空间中的一个点集从一个坐标系变换到另一个坐标系的有效方法.CGAffineTransform形变就是把二维形变使用一个三维矩阵来表示,其中第三列总是(0,0,1),形变通过前两列来控制,系统提供了CGAffineTransformMake结构体来控制形变
// 格式 CGAffineTransformMake(CGFloat a, CGFloat b, CGFloat c, CGFloat d, CGFloat tx, CGFloat ty)
该三维变换矩阵如下
通过变换矩阵左乘向量,将空间中的一个点集从一个坐标系变换到另一个坐标系中,计算方式如下
由此可知,其中tx用来控制在x轴方向上的平移,ty用来控制在y轴方向上的平移;a用来控制在x轴方向上的缩放,d用来控制在y轴方向上的缩放;abcd共同控制旋转
- 平移CGAffineTransformMakeTranslation原理
self.demoImageView.transform = CGAffineTransformMakeTranslation(100, 100); self.demoImageView.transform = CGAffineTransformMake(1, 0, 0, 1, 100, 100);
- 缩放CGAffineTransformMakeScale原理
self.demoImageView.transform = CGAffineTransformMakeScale(2, 0.5); self.demoImageView.transform = CGAffineTransformMake(2, 0, 0, 0.5, 0, 0);
- 旋转CGAffineTransformMakeRotation原理
self.demoImageView.transform = CGAffineTransformMakeRotation(M_PI*0.5); self.demoImageView.transform = CGAffineTransformMake(cos(M_PI * 0.5), sin(M_PI * 0.5), -sin(M_PI * 0.5), cos(M_PI * 0.5), 0, 0);
- 初始状态CGAffineTransformIdentity原理
self.demoImageView.transform = CGAffineTransformIdentity; self.demoImageView.transform = CGAffineTransformMake(1, 0, 0, 1, 0, 0);
- (void)pinch:(UIPinchGestureRecognizer *)pin {
switch (pin.state) {
case UIGestureRecognizerStateChanged:
NSLog(@"-------- %.2f", pin.scale);
pin.view.transform = CGAffineTransformScale(pin.view.transform, pin.scale, pin.scale);
pin.scale = 1.0;
break;
case UIGestureRecognizerStateEnded:
NSLog(@"++++++++ %.2f", pin.scale);
default:
break;
}
}
pinch使用心得
Pinch手势,即手指捏合/打开,属于多点触控的触发事件
先贴代码:
最开始时候我的代码非常的“简短”
简单的使用
- (IBAction)handlePinch:(UIPinchGestureRecognizer *)pincher
{
pincher.view.transform=CGAffineTransformScale(pincher.view.transform,pincher.scale,pincher.scale);
pincher.scale=1.0f;
}
结果图片的放大缩小始终是以图片的左上角为中心点缩放,而且由于我同时也实现了Pan操作,我的图片在pan到其他地方之后,触发pinch时,图片会瞬移到初始位置。找了一些网上的资料,发现这个transform是用来 存储图片信息 的,看了看官方解释:
也就是说,这个transform(CGAffineTransform类型,一个仿射矩阵,我也不是很理解)存储了 图片的中心点(图片所在layer的定位点);注意到其中的“or the layer's anchorPoint property if it was changed".我的理解是:如果你改变了图片的位置,但是你是简单的在view上改变的,那么transform的数据就不会更新,其center仍然是原来的点(因为layer的anchorPoint没变),所以就导致了图片会瞬移回到初始位置的情况。
下面的解释也提到,transform的变化是可以动画表现的,但是同样是以layer层的anchorPoint作为中心点。
最后一句的警告:如果此属性进行的不是恒等变换,那么frame属性值就是不确定的,应该被忽略(个人不太理解意思)。
关于为什么要这句: pincher.scale=1.0f 。再看看官方的解释:
首先要了解CGAffineTransformScale:
第一个参数是一个transform类型,意思是基于这个transform进行有关于scale的修改
第二个参数是x轴方向图片缩进比例,第三个则是y轴方向缩进比例。
scale factor,比例因子,是相对于屏幕上两个点的坐标确定的,我的理解是:由于函数handlePinch(pinch手势的响应函数)是在每个“瞬间”都调用的,所以它计算的是相邻两个瞬间两触控点的距离的变化程度确定scale的,但是这个scale是不会自动重置的,也就是说,如果不在每个“瞬间”重新初始化scale,那它将持续作用于transform,譬如第一个瞬间和第二个瞬间,pinch作捏合手势,scale被置为0.9f,那么x,y都变成0.9x,0.9y,若不初始化,那么此时手指就算没有动,都是由于系统仍然处于pinch这个gesture的过程中,所以每瞬间xy都会缩小,很快就变成无穷小了。
所以说,如果想要得到一个好的缩放功能,需要:1. 改变layer的中心点 2.重置scale。
所以说要对layer层进行直接操作。
我将手指触控的中点位置作为缩放中心,建立一个私有成员变量centerPoint:
@interfaceTouchEventViewController ()
@propertyCGPoint centerPoint;
@end
- (IBAction)handlePinch:(UIPinchGestureRecognizer *)pincher
{
if([pinchernumberOfTouches]<2)
return;
if(pincher.state==UIGestureRecognizerStateBegan)//set center
{
self.centerPoint = [pincher locationInView:pincher.view];
pincher.scale=1.0;
}
[pincher.view.layer setAffineTransform:CGAffineTransformScale(pincher.view.transform, pincher.scale, pincher.scale)];//change the scale of the transform in Layer.
pincher.scale=1.0;
//set the new center point
CGPoint nowPoint = [pincherlocationInView:pincher.view];
[pincher.view.layer setAffineTransform:
CGAffineTransformTranslate(pincher.view.transform, nowPoint.x-self.centerPoint.x,nowPoint.y-self.centerPoint.y)];
self.centerPoint=[pincherlocationInView:pincher.view];
}
代码很易懂:
如果说pinch手势刚开始,那么初始化centerPoint
再看看locationInView这个函数:
函数返回一个CGPoint,usually the centroid of the touches involved in the gesture.
也就是说,通过这个函数,就可以得到触控点在几何上的中心区域,由于这是在view中的坐标,因此会随着每个瞬间图片大小变化而变化。
需要注意的是centerPoint始终记录的是上一次Pinch结束后图片的位置!
另外,区别于直接改变transform的值,我们用layer层的方法setAffineTransform来对缩放后的图片进行一次重新定位,只有这样才能改变layer的anchorPoint的值。此时:setAffineTransform将以调用它的layer的anchorPoint为中心点进行缩放操作,所以现在得到de效果是,pinch操作将向图片的中心进行缩进、以图片中心为定点放大。
完成图片缩放之后,我们还要移动图片,让图片看起来就像是按照我们手指中心点缩放一样:
CGPoint nowPoint = [pincher locationInView:pincher.view];
[pincher.view.layersetAffineTransform:
CGAffineTransformTranslate([pincher.view.layeraffineTransform], nowPoint.x-self.centerPoint.x,nowPoint.y-self.centerPoint.y)];
self.centerPoint=[pincher locationInView:pincher.view];
nowPoint得到现在的触控中心,由于CGAffineTransformTranslate的后两个参数代表的分别是x,y轴方向上图片的位移量。
因此,我们需要进行坐标相减,让图片移动到我们想要的位置。
一样,setAffineTransform以现在layer的anchorPoint作为中心点进行移动,使得图片就像是以你的触控的中点为缩放中心一样。
其实,每个手指活动的瞬间,图片都以图片中心缩放,然后再进行位移,把手指中心的点重新放到你的手指中心。