最近项目中的一个小需求,要求图片同时进行旋转和缩放两种操作,做一个简单的总结,先看下效果图:
效果图.gif
需求分析
- 图片有两种缩放模式,一是点击右下角按钮进行旋转缩放,二是用手势进行旋转缩放;
- 点击按钮是单击操作,不是旋转和捏合手势的两指操作,所以只能用平移手势,通过平移手势的移动距离,来计算图片应该缩放的比例;
- 图片需要能同时进行多种手势操作;
手势操作
/** 旋转缩放参考点 */
@property (nonatomic, assign) CGPoint originalPoint;
/** 视图初始化宽高 */
@property (nonatomic, assign) CGFloat originalWidth;
@property (nonatomic, assign) CGFloat originalHeight;
//旋转手势
- (void)rotateAction:(UIRotationGestureRecognizer *)rotateGesture{
NSUInteger touchCount = rotateGesture.numberOfTouches;
if (touchCount <= 1) {
return;
}
CGPoint p1 = [rotateGesture locationOfTouch: 0 inView:self];
CGPoint p2 = [rotateGesture locationOfTouch: 1 inView:self];
CGPoint newCenter = CGPointMake((p1.x+p2.x)/2,(p1.y+p2.y)/2);
self.originalPoint = CGPointMake(newCenter.x/self.bounds.size.width, newCenter.y/self.bounds.size.height);
CGPoint oPoint = [self convertPoint:[self getRealOriginalPoint] toView:self.superview];
self.center = oPoint;
self.transform = CGAffineTransformRotate(self.transform, rotateGesture.rotation);
rotateGesture.rotation = 0;
oPoint = [self convertPoint:[self getRealOriginalPoint] toView:self.superview];
self.center = CGPointMake(self.center.x + (self.center.x - oPoint.x),
self.center.y + (self.center.y - oPoint.y));
}
//捏合手势
- (void)pinchAction:(UIPinchGestureRecognizer *)pinchGesture{
NSUInteger touchCount = pinchGesture.numberOfTouches;
if (touchCount <= 1) {
return;
}
CGPoint p1 = [pinchGesture locationOfTouch: 0 inView:self];
CGPoint p2 = [pinchGesture locationOfTouch: 1 inView:self];
CGPoint newCenter = CGPointMake((p1.x+p2.x)/2,(p1.y+p2.y)/2);
self.originalPoint = CGPointMake(newCenter.x/self.bounds.size.width, newCenter.y/self.bounds.size.height);
CGPoint oPoint = [self convertPoint:[self getRealOriginalPoint] toView:self.superview];
self.center = oPoint;
CGFloat scale = pinchGesture.scale;
if (scale < 1 && self.frame.size.width <= self.originalWidth/2) {
//当缩小到初始化宽高的一半时,停止缩小
}else{
self.transform = CGAffineTransformScale(self.transform, scale, scale);
[self fitCtrlScaleX:scale scaleY:scale];
}
oPoint = [self convertPoint:[self getRealOriginalPoint] toView:self.superview];
self.center = CGPointMake(self.center.x + (self.center.x - oPoint.x),
self.center.y + (self.center.y - oPoint.y));
pinchGesture.scale = 1;
}
originalPoint
为旋转缩放的参考点比例,默认是按视图中心旋转,即
self.originalPoint = CGPointMake(0.5, 0.5)
旋转和缩放手势,都需要先获取两个操作点的中心,获取新的参考点比例,重新确定视图的中心坐标(针对默认参考点不是中心点的情况)。
/** 获取参考点坐标 */
- (CGPoint)getRealOriginalPoint {
return CGPointMake(self.bounds.size.width * self.originalPoint.x,
self.bounds.size.height * self.originalPoint.y);
}
然后就是正常的操作,注意,在缩放的时候,四个角的控制按钮要相反的放缩,保证大小不变,如果有其他元素,同理。
/* 控制按钮保持大小不变 */
- (void)fitCtrlScaleX:(CGFloat)scaleX scaleY:(CGFloat)scaleY {
self.removeCtrl.transform = CGAffineTransformScale(self.removeCtrl.transform, 1/scaleX, 1/scaleY);
self.rotateCtrl.transform = CGAffineTransformScale(self.rotateCtrl.transform, 1/scaleX, 1/scaleY);
}
按钮操作缩放
#pragma mark - === 视图控制按钮 手势事件 ===
//缩放旋转
- (void)rotateCtrlPanGesture:(UIPanGestureRecognizer *)panGesture{
if (panGesture.state == UIGestureRecognizerStateBegan) {
self.lastCtrlPoint = [self convertPoint:self.rotateCtrl.center toView:self.superview];
return;
}
if (panGesture.state == UIGestureRecognizerStateEnded) {
return;
}
CGPoint ctrlPoint = [panGesture locationInView:self.superview];
[self scaleViewWithCtrlPoint:ctrlPoint];
[self rotateViewWithCtrlPoint:ctrlPoint];
self.lastCtrlPoint = ctrlPoint;
}
在控制按钮上添加平移手势,记录每一次平移的点ctrlPoint
,以及上一个平移点,就是self.lastCtrlPoint
#pragma mark - === 旋转 ===
- (void)rotateViewWithCtrlPoint:(CGPoint)ctrlPoint {
CGPoint oPoint = [self convertPoint:[self getRealOriginalPoint] toView:self.superview];
self.center = CGPointMake(self.center.x - (self.center.x - oPoint.x),
self.center.y - (self.center.y - oPoint.y));
float angle = atan2(self.center.y - ctrlPoint.y, ctrlPoint.x - self.center.x);
float lastAngle = atan2(self.center.y - self.lastCtrlPoint.y, self.lastCtrlPoint.x - self.center.x);
angle = - angle + lastAngle;
self.transform = CGAffineTransformRotate(self.transform, angle);
oPoint = [self convertPoint:[self getRealOriginalPoint] toView:self.superview];
self.center = CGPointMake(self.center.x + (self.center.x - oPoint.x),
self.center.y + (self.center.y - oPoint.y));
}
旋转的角度,根据上一个平移点和视图中心点的角度,与当前平移点和视图中心点的角度偏差,进行transform处理。
#pragma mark - === 缩放 ===
/* 等比缩放 */
- (void)scaleViewWithCtrlPoint:(CGPoint)ctrlPoint {
CGPoint oPoint = [self convertPoint:[self getRealOriginalPoint] toView:self.superview];
self.center = oPoint;
//上一个控制点距离中心的距离
CGFloat preDistance = [self distanceWithStartPoint:self.center endPoint:self.lastCtrlPoint];
//当前控制点距离中心的距离
CGFloat newDistance = [self distanceWithStartPoint:self.center endPoint:ctrlPoint];
CGFloat scale = newDistance / preDistance;
if (scale < 1 && self.frame.size.width <= self.originalWidth/2) {
//当缩小到初始化宽高一半时,停止缩小
}else{
self.transform = CGAffineTransformScale(self.transform, scale, scale);
[self fitCtrlScaleX:scale scaleY:scale];
}
oPoint = [self convertPoint:[self getRealOriginalPoint] toView:self.superview];
self.center = CGPointMake(self.center.x + (self.center.x - oPoint.x),
self.center.y + (self.center.y - oPoint.y));
}
/* 计算两点间距 */
- (CGFloat)distanceWithStartPoint:(CGPoint)start endPoint:(CGPoint)end {
CGFloat x = start.x - end.x;
CGFloat y = start.y - end.y;
return sqrt(x * x + y * y);
}
缩放也是类似,计算上一个平移点与中心点的距离preDistance
,以及当前平移点和中心点的距离newDistance
,那么两次平移距离的比例,就是视图缩放的比例。这里做了一个判断,在缩小到一半时停止继续变小。
GitHub:https://github.com/TonyHYH/HYHRotateScaleImageView.git
网友评论