swift 自定义图片大小裁剪

2,813 阅读4分钟
  • 裁剪区域枚举,标记裁剪位置tag值
fileprivate enum CropPosition: Int {
    case leftTop = 100
    case rightTop = 101
    case leftBottom = 102
    case rightBottom = 103
    case top = 104
    case left = 105
    case bottom = 106
    case right = 107
}
    // 最小裁剪宽高
    fileprivate let minCropWith = 100.0
    fileprivate let minCropHeight = 100.0
    
    // 被裁剪的图片
    fileprivate lazy var targetImageView:UIImageView = {
        let imageView = UIImageView(frame: CGRect(x: 10.0, y: 80.0, width: ScreenWith - 20.0, height: 400.0))
        let path = Bundle.main.path(forResource: "cropImage@2x.jpg", ofType: nil) ?? ""
        guard let image = UIImage(contentsOfFile: path) else {
            fatalError("图片没有找到")
        }
        imageView.image = image
        imageView.contentMode = .scaleToFill
        imageView.isUserInteractionEnabled = true
        return imageView
    }()
  • 裁剪区域view
    // 裁剪区域view
    fileprivate lazy var cropView:UIView = {
        let frame = CGRect(x: 0.0, y: 0.0, width: minCropWith, height: minCropHeight)
        let view = UIView(frame: frame)
        //        view.backgroundColor = .orange
        view.isUserInteractionEnabled = true
        let panGestureRec = UIPanGestureRecognizer(target: self, action: #selector(translation(pan:)))
        view.addGestureRecognizer(panGestureRec)
        
        let leftTopImage = UIImageView(image: UIImage(named: "corner_left_top"))
        leftTopImage.frame = CGRect(x: 0.0, y: 0.0, width: 20.0, height: 20.0)
        leftTopImage.autoresizingMask = [.flexibleBottomMargin,.flexibleRightMargin]
        leftTopImage.tag = CropPosition.leftTop.rawValue
        leftTopImage.isUserInteractionEnabled = true
        leftTopImage.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(moveAction(pan:))))
        view.addSubview(leftTopImage)
        
        let rightTopImage = UIImageView(image: UIImage(named: "corner_right_top"))
        rightTopImage.frame = CGRect(x: frame.maxX-20, y: 0, width: 20, height: 20)
        rightTopImage.autoresizingMask = [.flexibleBottomMargin,.flexibleLeftMargin]
        rightTopImage.tag = CropPosition.rightTop.rawValue
        rightTopImage.isUserInteractionEnabled = true
        rightTopImage.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(moveAction(pan:))))
        view.addSubview(rightTopImage)
        
        let leftBottomImage = UIImageView(image: UIImage(named: "corner_left_bottom"))
        leftBottomImage.frame = CGRect(x: 0, y: frame.maxY-20, width: 20, height: 20)
        leftBottomImage.autoresizingMask = [.flexibleTopMargin,.flexibleRightMargin]
        leftBottomImage.tag = CropPosition.leftBottom.rawValue
        leftBottomImage.isUserInteractionEnabled = true
        leftBottomImage.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(moveAction(pan:))))
        view.addSubview(leftBottomImage)
        
        let rightBottomImage = UIImageView(image: UIImage(named: "corner_right_bottom"))
        rightBottomImage.frame = CGRect(x: frame.maxX-20, y: frame.maxY-20, width: 20, height: 20)
        rightBottomImage.autoresizingMask = [.flexibleTopMargin,.flexibleLeftMargin]
        rightBottomImage.tag = CropPosition.rightBottom.rawValue
        rightBottomImage.isUserInteractionEnabled = true
        rightBottomImage.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(moveAction(pan:))))
        view.addSubview(rightBottomImage)
        
        let topView = UIView(frame: CGRect(x: 20, y: 0, width: frame.width-20*2, height: 20))
        //        topView.backgroundColor = UIColor.green
        topView.tag = CropPosition.top.rawValue
        topView.autoresizingMask = [.flexibleWidth,.flexibleBottomMargin]
        topView.isUserInteractionEnabled = true
        topView.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(moveAction(pan:))))
        view.addSubview(topView)
        
        let bottomView = UIView(frame: CGRect(x: 20, y: frame.height-20, width: frame.width-20*2, height: 20))
        //        bottomView.backgroundColor = UIColor.green
        bottomView.tag = CropPosition.bottom.rawValue
        bottomView.autoresizingMask = [.flexibleWidth,.flexibleTopMargin]
        bottomView.isUserInteractionEnabled = true
        bottomView.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(moveAction(pan:))))
        view.addSubview(bottomView)
        
        let leftView = UIView(frame: CGRect(x: 0, y: 20, width: 20, height: frame.height-20*2))
        //        leftView.backgroundColor = UIColor.green
        leftView.tag = CropPosition.left.rawValue
        leftView.autoresizingMask = [.flexibleHeight,.flexibleRightMargin]
        leftView.isUserInteractionEnabled = true
        leftView.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(moveAction(pan:))))
        view.addSubview(leftView)
        
        let rightView = UIView(frame: CGRect(x: frame.width-20, y: 20, width: 20, height: frame.height-20*2))
        //        rightView.backgroundColor = UIColor.green
        rightView.tag = CropPosition.right.rawValue
        rightView.autoresizingMask = [.flexibleHeight,.flexibleLeftMargin]
        rightView.isUserInteractionEnabled = true
        rightView.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(moveAction(pan:))))
        view.addSubview(rightView)
        
        return view
    }()
    override func viewDidLoad() {
        super.viewDidLoad()
        
        self.title = "图片裁剪"
        self.view.backgroundColor = .white
        
        self.navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .edit, target: self, action: #selector(cropAction))
        
        self.view.addSubview(self.targetImageView)
        
        self.targetImageView.addSubview(self.cropView)
        cropView.center = targetImageView.center
        
        self.resetCropMask()
    }
  • 遮罩层
    func resetCropMask() {
        let path = UIBezierPath(rect: targetImageView.bounds)
        let clearPath = UIBezierPath(rect: cropView.frame)
        path.append(clearPath)
        
        let layer = CAShapeLayer()
        layer.frame = targetImageView.bounds
        layer.fillColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.5).cgColor
        layer.strokeColor = UIColor.clear.cgColor
        layer.fillRule = .evenOdd
        layer.path = path.cgPath
        for item in targetImageView.layer.sublayers ?? [] {
            if item is CAShapeLayer {
                item.removeFromSuperlayer()
            }
        }
        targetImageView.layer.addSublayer(layer)
    }
  • 裁剪
    @objc func cropAction() {
        let frame = cropView.frame
        guard let image = targetImageView.image else {
            fatalError("图片数据有误")
        }
        let scale = image.size.width/ScreenWith
        guard let cropImage = image.cropping(to: CGRect(x: frame.origin.x*scale, y: frame.origin.y*scale, width: frame.width*scale, height: frame.height*scale)) else {
            fatalError("图片剪裁出错")
        }
        debugPrint(cropImage)
        // 保存到相册
        var localID = ""
        PHPhotoLibrary.shared().performChanges {
            let result = PHAssetChangeRequest.creationRequestForAsset(from: cropImage)
            let assetPlaceholder = result.placeholderForCreatedAsset
            localID = assetPlaceholder?.localIdentifier ?? "" // 保存保存标志符
        } completionHandler: { (success, error) in
            if success == true {
                debugPrint("保存成功")
                //通过标志符获取对应的资源
                let assetResult = PHAsset.fetchAssets(withLocalIdentifiers: [localID], options: nil)
                let asset = assetResult.firstObject
                let options = PHContentEditingInputRequestOptions()
                options.canHandleAdjustmentData = {(adjustmeta: PHAdjustmentData) -> Bool in
                    return true
                }
                //获取保存的图片路径
                asset?.requestContentEditingInput(with: options, completionHandler: { (contentEditingInput, info) in
                    let uri = contentEditingInput?.fullSizeImageURL?.absoluteString ?? ""
                    debugPrint("保存的图片路径 = \(uri)")
                })
                
            } else {
                NSLog("保存失败: \(String(describing: error?.localizedDescription))")
            }
        }
    }
  • 移动整个裁剪区域
@objc func translation(pan:UIPanGestureRecognizer) {
        
        var point = pan.location(in: targetImageView)
        if point.x < 0 {point.x = 0}
        if point.x > targetImageView.frame.width {point.x = targetImageView.frame.width}
        if point.y < 0 {point.y = 0}
        if point.y > targetImageView.frame.height {point.y = targetImageView.frame.height}
        
        if pan.state == .changed {
            var frame = cropView.frame
            let minX: CGFloat = 0.0
            let minY: CGFloat = 0.0
            let maxX = targetImageView.frame.width-frame.width
            let maxY = targetImageView.frame.height-frame.height
            var x = point.x-frame.width/2
            if x < minX {
                x = minX
            }
            if x > maxX {
                x = maxX
            }
            var y = point.y-frame.height/2
            if y < minY {
                y = minY
            }
            if y > maxY {
                y = maxY
            }
            frame.origin.x = x
            frame.origin.y = y
            cropView.frame = frame
            resetCropMask()
        }
        
    }
  • 跳转裁剪区域边框
@objc func moveAction(pan:UIPanGestureRecognizer) {
        
        guard let panView = pan.view else { return }
        
        var point = pan.location(in: targetImageView)
        if point.x < 0 {point.x = 0}
        if point.x > targetImageView.frame.width {point.x = targetImageView.frame.width}
        if point.y < 0 {point.y = 0}
        if point.y > targetImageView.frame.height {point.y = targetImageView.frame.height}
        
        if pan.state == .changed {
            if panView.tag == CropPosition.leftTop.rawValue {
                var frame = cropView.frame
                let minX: CGFloat = 0.0
                let minY: CGFloat = 0.0
                let maxX = frame.maxX - CGFloat(minCropWith)
                let maxY = frame.maxY - CGFloat(minCropHeight)
                var x = point.x
                if x < minX {
                    x = minX
                }
                if x > maxX {
                    x = maxX
                }
                var y = point.y
                if y < minY {
                    y = minY
                }
                if y > maxY {
                    y = maxY
                }
                frame = CGRect(x: x, y: y, width: (frame.origin.x-x)+frame.width, height: (frame.origin.y-y)+frame.height)
                cropView.frame = frame
                resetCropMask()
            } else if panView.tag == CropPosition.left.rawValue {
                var frame = cropView.frame
                let minX: CGFloat = 0.0
                let maxX = frame.maxX - CGFloat(minCropWith)
                var x = point.x
                if x < minX {
                    x = minX
                }
                if x > maxX {
                    x = maxX
                }
                frame = CGRect(x: x, y: frame.origin.y, width: (frame.origin.x-x)+frame.width, height: frame.height)
                cropView.frame = frame
                resetCropMask()
            } else if panView.tag == CropPosition.leftBottom.rawValue {
                var frame = cropView.frame
                let minX: CGFloat = 0.0
                let minY: CGFloat = frame.minY+CGFloat(minCropHeight)
                let maxX = frame.maxX-CGFloat(minCropWith)
                let maxY = targetImageView.frame.height
                var x = point.x
                if x < minX {
                    x = minX
                }
                if x > maxX {
                    x = maxX
                }
                var y = point.y
                if y < minY {
                    y = minY
                }
                if y > maxY {
                    y = maxY
                }
                frame = CGRect(x: x, y: frame.origin.y, width: (frame.origin.x-x)+frame.width, height: y-frame.origin.y)
                cropView.frame = frame
                resetCropMask()
            } else if panView.tag == CropPosition.top.rawValue {
                var frame = cropView.frame
                let minY: CGFloat = 0.0
                let maxY = frame.maxY-CGFloat(minCropHeight)
                var y = point.y
                if y < minY {
                    y = minY
                }
                if y > maxY {
                    y = maxY
                }
                frame = CGRect(x: frame.origin.x, y: y, width: frame.width, height: (frame.origin.y-y)+frame.height)
                cropView.frame = frame
                resetCropMask()
            } else if panView.tag == CropPosition.rightTop.rawValue {
                var frame = cropView.frame
                let minX: CGFloat = frame.minX+CGFloat(minCropWith)
                let minY: CGFloat = 0.0
                let maxX = targetImageView.frame.width
                let maxY = frame.maxY-CGFloat(minCropHeight)
                var x = point.x
                if x < minX {
                    x = minX
                }
                if x > maxX {
                    x = maxX
                }
                var y = point.y
                if y < minY {
                    y = minY
                }
                if y > maxY {
                    y = maxY
                }
                frame = CGRect(x: frame.origin.x, y: y, width: x-frame.origin.x, height: (frame.origin.y-y)+frame.height)
                cropView.frame = frame
                resetCropMask()
            } else if panView.tag == CropPosition.right.rawValue {
                var frame = cropView.frame
                let minX: CGFloat = frame.minX+CGFloat(minCropWith)
                let maxX = targetImageView.frame.width
                var x = point.x
                if x < minX {
                    x = minX
                }
                if x > maxX {
                    x = maxX
                }
                frame = CGRect(x: frame.origin.x, y: frame.origin.y, width: x-frame.origin.x, height: frame.height)
                cropView.frame = frame
                resetCropMask()
            } else if panView.tag == CropPosition.rightBottom.rawValue {
                var frame = cropView.frame
                let minX: CGFloat = frame.minX+CGFloat(minCropWith)
                let minY: CGFloat = frame.minY+CGFloat(minCropWith)
                let maxX = targetImageView.frame.width
                let maxY = targetImageView.frame.height
                var x = point.x
                if x < minX {
                    x = minX
                }
                if x > maxX {
                    x = maxX
                }
                var y = point.y
                if y < minY {
                    y = minY
                }
                if y > maxY {
                    y = maxY
                }
                frame = CGRect(x: frame.origin.x, y: frame.origin.y, width: x-frame.origin.x, height: y-frame.origin.y)
                cropView.frame = frame
                resetCropMask()
            } else if panView.tag == CropPosition.bottom.rawValue {
                var frame = cropView.frame
                let minY: CGFloat = frame.minY+CGFloat(minCropHeight)
                let maxY = targetImageView.frame.height
                var y = point.y
                if y < minY {
                    y = minY
                }
                if y > maxY {
                    y = maxY
                }
                frame = CGRect(x: frame.origin.x, y: frame.origin.y, width: frame.width, height: y-frame.origin.y)
                cropView.frame = frame
                resetCropMask()
            }
        }
        
    }
  • UIImage Extension
extension UIImage {
    
    //裁剪image,rect是相对于image.size大小
    public func cropping(to rect: CGRect) -> UIImage? {
        guard self.size.width > rect.origin.x else {
            return nil
        }
        guard self.size.height > rect.origin.y else {
            return nil
        }
        let scaleRect = CGRect(x: rect.origin.x*self.scale, y: rect.origin.y*self.scale, width: rect.width*self.scale, height: rect.height*self.scale)
        if let cgImage = self.cgImage?.cropping(to: scaleRect) {
            let cropImage = UIImage(cgImage: cgImage, scale: self.scale, orientation: .up)
            return cropImage
        } else {
            return nil
        }
    }
}
  • 手指滑动截图
// 被裁剪的图片
    fileprivate lazy var targetImageView:UIImageView = {
        let imageView = UIImageView(frame: CGRect(x: 10.0, y: 80.0, width: ScreenWith - 20.0, height: 400.0))
        let path = Bundle.main.path(forResource: "cropImage@2x.jpg", ofType: nil) ?? ""
        guard let image = UIImage(contentsOfFile: path) else {
            fatalError("图片没有找到")
        }
        imageView.image = image
        imageView.contentMode = .scaleToFill
        imageView.isUserInteractionEnabled = true
        
        imageView.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(crop(pan:))))
        
        return imageView
    }()
    lazy var startPoint = CGPoint(x: 0, y: 0)
    
    @objc func crop(pan:UIPanGestureRecognizer) {
        let point = pan.location(in: targetImageView)
        if pan.state == .began {
            startPoint = point
        } else if pan.state == .changed {
            let newOrigin = CGPoint(x: min(point.x, startPoint.x), y: min(point.y, startPoint.y))
            let newSize = CGSize(width: abs(point.x-startPoint.x), height: abs(point.y-startPoint.y))
            let frame = CGRect(origin: newOrigin, size: newSize)
            cropView.frame = frame
        } else if pan.state == .ended {
            let frame = cropView.frame
            if let image = targetImageView.image {
                let scale = image.size.width/ScreenWith
                let cropImage = image.cropping(to: CGRect(x: frame.origin.x*scale, y: frame.origin.y*scale, width: frame.width*scale, height: frame.height*scale))
                debugPrint(cropImage!)
            }
            
            cropView.frame = CGRect(x: 0, y: 0, width: 0, height: 0)
        }
    }