iOS 图片的同时旋转缩放

作者: Tony_HYH | 来源:发表于2019-07-11 15:43 被阅读5次
    占位图.jpeg

    最近项目中的一个小需求,要求图片同时进行旋转和缩放两种操作,做一个简单的总结,先看下效果图:


    效果图.gif

    需求分析

    1. 图片有两种缩放模式,一是点击右下角按钮进行旋转缩放,二是用手势进行旋转缩放;
    2. 点击按钮是单击操作,不是旋转和捏合手势的两指操作,所以只能用平移手势,通过平移手势的移动距离,来计算图片应该缩放的比例;
    3. 图片需要能同时进行多种手势操作;

    手势操作

    /** 旋转缩放参考点 */
    @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

    相关文章

      网友评论

        本文标题:iOS 图片的同时旋转缩放

        本文链接:https://www.haomeiwen.com/subject/ykurkctx.html