照片选择区域功能的另一实现: 加动效

照片选择区域功能的另一实现: 加动效

IOS小彩虹2021-08-22 21:55:44160A+A-

前言:

之前写了两份相关的,

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

仿扫描全能王的选择区域功能:拍照,旋转

思路就是从 touch 相关手势,识别点,直接绘制

不方便,简单地添加动画

本文介绍另一思路,通过控件

有参考 rzmn/CropView

思路具体

1, 拿到点,还是同样的三个方法,

touchesBegan, touchesMoved, touchesEnded

2, 视图层级
  • 功能视图 cropView, 添加在 image view 上,

功能视图 cropView 中, 识别手势

  • 画线,四条边,

var areaQuadrangle = SEAreaView()

添加在功能视图 cropView

2.1, 方便做动画,四个角都是控件, 添加在功能视图 cropView

class CornerView: UIView

拖动某个角,那个角落的圆圈变大;

放开那个角,那个角落的圆圈缩回

3,坐标的对应关系

imageView 设置为 sizeToFit,

默认,imageView 的 size > 上面看到的 image 的 size

( 当然,image.size 图片数据的 size 很大的 )

功能视图 cropView 的 frame = 上面看到的 image 的 frame

那么画线,四条边,var areaQuadrangle = SEAreaView() 的 frame = 功能视图 cropView 的 bounds

坐标: var cornerLocations : [CGPoint]?

这个是外部第一次传入的,或者外部需要修改传入的,

同时,他也是手势移动 touchesMoved 识别到的

实现

配置

func configure(corners imageView: UIImageView) {
        // 配置功能视图
        self.imageView = imageView
        self.imageView?.isUserInteractionEnabled = true
        imageView.addSubview(self)
        // 初始化
        for subview in subviews {
            if subview is SECornerView {
                subview.removeFromSuperview()
            }
        }
        // 配置四个角
        for _ in 0..<Setting.std.cornerCount {
            let corner = SECornerView(frame: CGRect(x: 0, y: 0, width: Setting.std.cornerSize, height: Setting.std.cornerSize))
            addSubview(corner)
            cornerViews.append(corner)
        }
        // 配置四条边
        areaQuadrangle.backgroundColor = .clear
        addSubview(areaQuadrangle)
    }

功能视图 cropView 的 frame = 上面看到的 image 的 frame

第一次进来,实现上面那句

因为一般在 viewDidLoad 里面配置,这时候,imageView 的 frame 还没确定下来

控件内部实现的合适时机是,func layoutSubviews(), 需要走一次

public override func layoutSubviews() {
        super.layoutSubviews()
        guard first else {
            return
        }
        if let imgsize = imageView?.image?.size, let imageBounds = imageView?.bounds {
            let f = AVMakeRect(aspectRatio: imgsize, insideRect: imageBounds)
            frame = f
        }
        first = false
        // 从 bounds, 算出内部四个角落的点
        let f = bounds
        let first = f.origin
        let rhsTop = CGPoint(x: first.x + f.width, y: first.y)
        let lhsHip = CGPoint(x: first.x, y: first.y + f.height)
        let end = CGPoint(x: rhsTop.x, y: lhsHip.y)
        let dots = [first, rhsTop, end, lhsHip]
        self.cornerLocations = dots
        areaQuadrangle.frame = bounds
        update(scale: nil)
        cornerOnTouch = nil
    }

AVMakeRect 方法挺好用的,直接把 imageView 内部 image sizeToFit 的 size, 算出来了

手势移动识别

当然,至少还有两个手势


override public func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        super.touchesMoved(touches, with: event)
        guard let touchIdx = cornerOnTouch, touches.count == 1, let first = touches.first, let cornerLocations = cornerLocations, let img = imageView?.image else { return }
        // 拿到点,
        // 通过两个点的距离 + 起始点 cornerLocations[touchIdx].x , 稍微繁琐
        let from = first.previousLocation(in: self)
        let to = first.location(in: self)
        let derivative = CGPoint(x: to.x - from.x, y: to.y - from.y)
        
        let rawPt = CGPoint(x: cornerLocations[touchIdx].x + derivative.x, y: cornerLocations[touchIdx].y + derivative.y)
        // 确保不越界
        let newCenterOnImage = rawPt.normalized(size: img.size)
        // 更新数据
        self.cornerLocations?[touchIdx] = newCenterOnImage
        // 效果
        update(scale: nil)
    }

效果

func update(scale isBigger: Bool?) {
        guard let touchIdx = cornerOnTouch else {
            return
        }
        // 更新位置
        // 更新画线
        pairPositionsAndViews()
        if let bigger = isBigger{
            switch bigger {
            case true:
                // 角落圆圈,动画缩放
                cornerViews[touchIdx].scaleUp()
            case false:
                cornerViews[touchIdx].scaleDown()
            }
        }
        // 角落圆圈,涂色
        for corner in cornerViews {
            corner.layer.borderColor = (isPathValid ? Setting.std.goodAreaColor : Setting.std.badAreaColor).cgColor
        }
    }

更新位置


func pairPositionsAndViews() {
        // 控件更新位置,就是改中心点
        if let cornerPositions = self.cornerLocations {
            for i in 0 ..< Setting.std.cornerCount {
                self.cornerViews[i].center = CGPoint(x: cornerPositions[i].x, y: cornerPositions[i].y)
            }
        }
        // 更新画线
        self.areaQuadrangle.fill(path: path)
    }
动画

控件动画,简单


class CornerView: UIView {
    // 放大动画
    func scaleUp() {
        UIView.animate(withDuration: 0.15, animations: {
            self.layer.borderWidth = 0.5
            self.transform = CGAffineTransform.identity.scaledBy(x: 2, y: 2)
        })
    }
    // 缩小动画类似
}

交叉重联

如果一个点拖动,越过对面的边,导致交叉了

如果是凸四边形,就重新连接

这段逻辑的触发时机是 touchesEnded

override public func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        super.touchesEnded(touches, with: event)
        guard cornerOnTouch != nil, touches.count == 1 else {
            return
        }
        // 	交叉重联
        sortPointClockwise()
        //  初始化
        update(scale: false)
        cornerOnTouch = nil
    }

交叉重联,在篇首的博客里面有讲解,这里略

github repo

点击这里复制本文地址 以上内容由权冠洲的博客整理呈现,请务必在转载分享时注明本文地址!如对内容有疑问,请联系我们,谢谢!

支持Ctrl+Enter提交

联系我们| 本站介绍| 留言建议 | 交换友链 | 域名展示
本站资源来自互联网收集,仅供用于学习和交流,请遵循相关法律法规,本站一切资源不代表本站立场,如有侵权、后门、不妥请联系本站删除

权冠洲的博客 © All Rights Reserved.  Copyright quanguanzhou.top All Rights Reserved
苏公网安备 32030302000848号   苏ICP备20033101号-1
本网站由 提供CDN/云存储服务

联系我们