拍照,用系统的,简单

本文主要是讲,选择区域的旋转

  • 照片的旋转,用仿射变换
  • 选择区域的旋转,就是旋转坐标系中的点,有一个坐标变换

手机 UIKit 框架下, view 的坐标系原点,是 view 的左上角

旋转坐标系中的点,一般坐标系用中心,方便


拍照,略。开始旋转选择区域

1, 摆放好

上面有两个视图,放照片的 UIImageView, 和照片上拖拽视图 sketch

布局没有采用,约束

因为旋转的时候,他们的 frame 变来变去,

直接手动计算 frame, 更加的直观

         if let img = image{
            let s = img.size.size(in: measure.s)
        
            imgView.frame.size = s
            imgView.center = measure.center
            sketch.frame = imgView.frame
            // 照片
            view.addSubview(imgView)
            // 照片上的,拖拽区域
            view.addSubview(sketch)
            // ...
        }

系统拍照,默认分辨率, 4 : 3

为了简化计算,这里 imageView 的高/宽 = 其 image 的高/宽

拍照的图片,很大,合适的放在指定的小区域中

extension CGSize{
    
    func size(in std: CGSize) -> CGSize{
        
        let hRatio = height / std.height
        let wRatio = width / std.width
        
        let reSolution = height / width
        
        
        let s: CGSize
        
        if hRatio > wRatio{
            //  图片更窄
            s = CGSize(width: std.height / reSolution, height: std.height)
        }
        else{
            //  图片更宽
            s = CGSize(width: std.width, height: std.width * reSolution)
        }
        return s
        
    }
}

2, 旋转

从图 a 到图 b , 是一个右旋

  • 旋转选择区域视图,为了简化计算,旋转选择区域的 frame 与看到的图片贴合

交换选择区域的宽和高,就是修改尺寸 size,

恢复其原来的中心点 center , 就是更新其原点

  • 旋转选择区域视图,上的四个拖拽点,

先在原来的坐标系中,旋转角度,

选择区域视图的原点变了,四个拖拽点旋转角度后,还需一个平移的补偿



    // 记录当前的角度
    var angle: CGFloat = 0

    @objc func rightTurn(){
        rotate(with: .rhs)
    }
    
    
    
    func rotate(with direction: RotateOpt) {
        
        let sizeOld = sketch.frame.size
        let originOld = sketch.frame.origin
        let center = sketch.center
  
        switch direction {
        case .lhs:
            
            // 逆时针
            
            angle -= 1
            
            
            sketch.defaultPoints.update(clockwize: false, by: sizeOld)
        case .rhs:
            
            // 顺时针
            
            angle += 1
            // 旋转四个点
            sketch.defaultPoints.update(clockwize: true, by: sizeOld)
            
        }
        
        
        // 图片视图,直接仿射变换
        imgView.transform = CGAffineTransform(rotationAngle: ImgSingleAngle.time * angle)
        
        sketch.frame.size = CGSize(width: sizeOld.height, height: sizeOld.width)
       
       
        sketch.center = center
        let originNew = sketch.frame.origin
        // 再补偿四个点
        sketch.defaultPoints.patch(vector: originNew - originOld)
        
        
        // 更新绘制
        sketch.reloadData()
        
    }

旋转点的坐标,通过角度的变化,计算


    // 拿 size, 去算中心点
    mutating
    func update(clockwize beC: Bool, by area: CGSize){
        let lhsTop: CGPoint, rhsTop: CGPoint, rhsBottom: CGPoint, lhsBottom: CGPoint
        let center = CGPoint(x: area.width / 2, y: area.height / 2 )
        if beC{
            lhsTop = clockwize(rightTurn: leftTop, forCoordinate: center)
            // ...
        }
        else{
            lhsTop = antiClockwize(leftTurn: leftTop, forCoordinate: center)
            // ...
        }
        leftTop = lhsTop
        // ...
        
       
    }
    
    // 顺时针旋转
    private
      func clockwize(rightTurn target: CGPoint, forCoordinate origin: CGPoint) -> CGPoint {
          let dx = target.x - origin.x
          let dy = target.y - origin.y
          let radius = sqrt(dx * dx + dy * dy)
          let azimuth = atan2(dy, dx) // in radians
          let x = origin.x - radius * sin(azimuth)
          let y = origin.y + radius * cos(azimuth)
          return CGPoint(x: x, y: y)
      }

3, 旋转加强,为了更好的利用手机区域,图片竖着更大,横着稍小一些

图片视图,仿射变换,是旋转 rotate + 缩放 scale

选择区域视图 sketch, 不能简单的交换宽和高,

稍麻烦了一些

选择区域视图 sketch 的 frame, 中心点不变,他的 size 有两个选择,切换就好了

在上一步的基础上,计算选择区域视图 sketch 的四个坐标
  • 竖着的,转横着的,大变小,上一步的坐标,缩小就行
  • 横着的,转竖着的,小变大,先放大,再用标准坐标 ( 竖着的尺寸,交换后 ),投入计算
func rotate(with direction: RotateOpt) {
        guard let img = image else {
            return
        }
        
        let imgRatio = img.size.height / img.size.width
        let const = 4.0/3
        print(imgRatio, const)
        
        let sizeOld = sketch.frame.size
        let originOld = sketch.frame.origin
        let center = sketch.center
        let bigS = img.size.size(in: measure.s)
        let clockwize: Bool
        switch direction {
        case .lhs:
            
            // 逆时针
            
            angle -= 1
            clockwize = false
            
            
        case .rhs:
            
            // 顺时针
            
            angle += 1
            clockwize = true
            
            // 下一步,对 UI 的修改,影响上一步
            
        }
        
        
        
        var ratio: CGFloat = 1
        
        let smallS = img.size.size(by: measure.horizontal)
        
        var imgTransform = CGAffineTransform(rotationAngle: ImgSingleAngle.time * angle)
        if Int(angle) % 2 == 1{
            ratio = smallS.width / bigS.height
            imgTransform = imgTransform.scaledBy(x: ratio, y: ratio)
            sketch.frame.size = smallS
           
        }
        else{
            ratio = bigS.height / smallS.width
            sketch.frame.size = bigS
            
        }
     
        // 旋转四个拖拽的点之前,先复位
        
        // 小的,变大的,的时候,需要操作
        if Int(angle) % 2 == 0{
          
            sketch.defaultPoints.scale(r: ratio, forS: sizeOld)
        }

        // 旋转,和平移补偿之前,先化为标注的,也就是竖着的尺寸

        sketch.defaultPoints.update(clockwize: clockwize, by: sizeOld)
        imgView.transform = imgTransform
        
       
        sketch.center = center
        let originNew = sketch.frame.origin
        // 补偿,跟上一步一样
        sketch.defaultPoints.patch(vector: originNew - originOld)
        
        
        //  四个拖拽的点, 属于正常标准的图片
        
        // 横着摆放,大变小
        if Int(angle) % 2 == 1{
        
            sketch.defaultPoints.scale(r: ratio, forS: sizeOld)
        }
        
        // 绘制
        sketch.reloadData()
        
    }

前文, 低仿扫描全能王的选择区域功能

github repo


black_pearl
79 声望5 粉丝

火星时代, 未来是太空