Swift-截图图片指定区域, 并生成新图片

作者: 直男程序员 | 来源:发表于2018-09-13 17:47 被阅读47次
截取图_1.png

项目中遇到了截图指定区域图片的功能, 比如一张全车图, 用户可以在手机上自由的画圈, 画完后要截取到画圈区域的图片, 然后进行处理. 经过试验, 现在将我做的demo和思路这这里和大家分享下.

一.首先分析下思路

1.使用UIImageView显示要被裁剪的图片;
2.因为需要截取图片, 由于在imageView上直接截取的话, 会由于图片像素的原因有误差, 因为截图是在layer上截取, 是cgImage, 所以需要先获得当前图片区大小像素的图片;
3.因为为了让用户使用直观,涉及到划线, [注]UIImageView不能划线, 所以要创建一个图片大小的UIView用于画线;
4.根据画线的区域可以获取到最左右前后四个点, 从而获取到一个区域矩形;
5.根据获取到的画线区域截图图片.

整体思路就是这样的, 大家可以先理解下思路, 接下来详细给大家讲下实现.

二.创建需要的控件
    fileprivate var img_Car: UIImageView! //车img
    fileprivate var view_GetImgBg: UIView! //图片bg,用于截取当前大小的image
    fileprivate var img_GetBg: UIImage! //获取当前的图片, 用于截图, 这样避免图片像素影响, 使用当前显示的大小
    fileprivate var view_Crop: LineDrawView! //专门用于画线 因为img不能画线
    fileprivate var img_Crop: UIImageView! //根据花圈区域截的图
    fileprivate var array_TouchCrop = [CGPoint]() //画圈点 相对于背景view的位置
    fileprivate var array_TouchImage = [CGPoint]() //相对于车图的位置

因为需要相对于图片区域截图, 所以要使用frame布局, 不要使用自动布局.

1.创建图片的UIImageView和图片大小的UIView获取当前大小像素的图片

        let width_Img = kScreenW / 375 * 175
        let margin_LeftImg = (kScreenW - width_Img) / 2
        
        view_GetImgBg = UIView()
        addSubview(view_GetImgBg)
        view_GetImgBg.frame = CGRect(x: margin_LeftImg, y:  margin_TopMid, width: width_Img, height: kScreenH - margin_TopMid * 2)
        
        img_Car = UIImageView()
        img_Car.contentMode = .scaleToFill
        img_Car.isUserInteractionEnabled = true
        img_Car.frame = CGRect(x: 0, y: 0, width: view_GetImgBg.frame.size.width, height: view_GetImgBg.frame.size.height)
        view_GetImgBg.addSubview(img_Car)

2.获取当前图片大小像素的图片用于截取图片

   //获取当前显示图片大小的图
    func getCurrentFrameImage() -> UIImage {
        
        UIGraphicsBeginImageContext(view_GetImgBg.bounds.size)
        
        view_GetImgBg.layer.render(in: UIGraphicsGetCurrentContext()!)
        let imgae = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return imgae!
    }

3.创建用于画线的UIView

        view_Crop = LineDrawView(frame: .zero, arrayPath: array_TouchCrop)
        addSubview(view_Crop)
        view_Crop.frame = CGRect(x: 0, y: 0, width: kScreenW, height: kScreenH)

4.创建UIImageView用于显示最后截取的图片

        img_Crop = UIImageView()
        img_Crop.isHidden = true
        view_GetImgBg.addSubview(img_Crop)
三.用于画线的UIView

因为UIImageView不能画线, 即使用CGGraphics核心绘画,所以需要专门创建相同大小的背景UIView用于绘画.

demo中我封装的一个UIView专门用于绘画, 解耦了下.

class LineDrawView: UIView {

    var array_Path = [CGPoint]()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
    }
    
    convenience init(frame: CGRect, arrayPath: [CGPoint]) {
        self.init(frame: frame)
        
        array_Path = arrayPath
        backgroundColor = UIColor.clear
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override func draw(_ rect: CGRect) {
        
        super.draw(rect)
        
        //获取绘图上下文
        guard let context = UIGraphicsGetCurrentContext() else{return}
        if array_Path.count == 0 {return}
        
        //创建并设置路径
        let pathRef: CGMutablePath = CGMutablePath()
        pathRef.move(to: array_Path[0])
        pathRef.addLines(between: array_Path)
        
        //添加路径到图形上下文
        context.addPath(pathRef)
        
        //设置笔触的颜色和宽度
        context.setStrokeColor(UIColor.red.cgColor)
        context.setLineWidth(4)
        
        //绘制路径
        context.strokePath()
    }

}
四.在Touch中处理轨迹

因为是要获取用户画线的区域, 所以在touch中获取到用户活动的点即可.
因为背景画圈的大小和图片的大小不一致, 所以项目中使用了两个数组来存储相对于图片和画圈背景view的点.

1.两个存储点的数组:

    fileprivate var array_TouchCrop = [CGPoint]() //画圈点 相对于背景view的位置
    fileprivate var array_TouchImage = [CGPoint]() //相对于车图的位置

2.每次TouchBegin 请空数组, 初始化数据和页面

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        initCropImageStatus()
    }

  func initCropImageStatus() {
        array_TouchCrop.removeAll()
        array_TouchImage.removeAll()
        createPath()
        img_Car.isHidden = false
        img_Crop.isHidden = true
    }

3.TouchesMove中使用数组存储移动的点

   override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        array_TouchImage.append(touches.first!.location(in: img_Car))
        array_TouchCrop.append(touches.first!.location(in: view_Crop))
    }

4.在画圈结束后, 根据存储的点计算区域, 进行截图

 override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        
        if array_TouchCrop.count == 0{return}
        
        createPath()
        
        var topPoint: CGPoint = array_TouchImage[0]
        var righttPoint: CGPoint = array_TouchImage[0]
        var bottomPoint: CGPoint = array_TouchImage[0]
        var leftPoint: CGPoint = array_TouchImage[0]
        
        for item in array_TouchImage{
            if item.y < topPoint.y{
                topPoint = item
            }
            if item.x > righttPoint.x{
                righttPoint = item
            }
            if item.y > bottomPoint.y{
                bottomPoint = item
            }
            if item.x < leftPoint.x{
                leftPoint = item
            }
        }
        
        //设置最小的画圈范围, 小于10则不进行处理
        if bottomPoint.y - topPoint.y <= 10{
            return
        }
        if righttPoint.x - leftPoint.x <= 10{
            return
        }
        
        let frame = CGRect(x: leftPoint.x, y: topPoint.y, width: righttPoint.x - leftPoint.x, height: bottomPoint.y - topPoint.y)
        
        img_Crop.frame = frame
        img_Crop.image = clipWithImageRect(clipFrame: frame, bgImage: img_GetBg)
        img_Car.isHidden = true
        img_Crop.isHidden = false
        
        if let block = getImage{
            block(img_Crop.image ?? UIImage.init())
        }
    }
五.绘画及截取图片

1.根据存储的画圈点进行绘制, 绘制其实就是调取UIViewdrawRect方法进行重绘.

    func createPath() {
        
        view_Crop.array_Path = array_TouchCrop
        view_Crop.setNeedsDisplay() //重绘
    }

2.根据计算的区域frame和用于裁剪的图片来进行裁剪:

    func clipWithImageRect(clipFrame: CGRect, bgImage: UIImage) -> UIImage {
        
        let rect_Scale = CGRect(x: clipFrame.origin.x, y: clipFrame.origin.y, width: clipFrame.size.width, height: clipFrame.size.height)
        
        let cgImageCorpped = bgImage.cgImage?.cropping(to: rect_Scale)
        let img_Clip = UIImage.init(cgImage: cgImageCorpped!, scale: 1, orientation: UIImageOrientation.up)
        
        return img_Clip
    }

到这里就大功告成了, 因为我为了解耦, 将这个过程封装在一个UIView里面, 方便使用, 结束了可以通过代理,闭包等方式传出去, 此 demo采用了闭包的形式, 截图的图片有了, 就可以处理了.

详细的代码可以到github下载: Github下载地址

相关文章

网友评论

    本文标题:Swift-截图图片指定区域, 并生成新图片

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