有些时候我们需要处理拍照后的图片截取操作,比如在上传用户头像的时候,用户上传的图片可能是很长的一张图片,但这样最后显示的头像就不会太完美。我们需要在用户上传头像的时候,有一个可以截取图片的功能。
需求分析
从上图直观看出,随着用户的触摸移动,会创建一个透明遮罩来表示需要截取的区域,然后截取了区域里的图片。因为我这里demo只是为了掩饰,imageView和控制器view约束尺寸一样了,会有放大效果,裁剪图片是正确的。
实现步骤
随着用户触摸移动,根据这个我们可以使用pan手势来实现,手势会有状态,我们这里可以记录开始触摸时的点,触摸中改变的点,触摸结束时的点。然后根据这些点,可以很容易的计算出我们需要裁剪的区域。
黑色透明区域,只是一个普通的,黑色透明的view,在用户移动手指时,动态计算截取区域并设置这个view的frame值。当松手的时候,裁剪图片并清除view的frame值即可。
代码实现
@IBOutlet weak var imageView: UIImageView! private var startPoint = CGPointZero override func viewDidLoad() { super.viewDidLoad() // 给当前控制器view添加一个pan手势 let panGesture = UIPanGestureRecognizer(target: self, action: #selector(didPan(_:))) view.addGestureRecognizer(panGesture) } @objc private func didPan(pan: UIPanGestureRecognizer) { let endPoint = pan.locationInView(view) let clipRect = CGRect(origin: startPoint, size: CGSize(width: endPoint.x - startPoint.x, height: endPoint.y - startPoint.y)) if pan.state == .Began { startPoint = pan.locationInView(view) } else if pan.state == .Changed { clipRectView.frame = clipRect } else if pan.state == .Ended { // 开启位图上下文,并裁剪指定区域 UIGraphicsBeginImageContextWithOptions(imageView.bounds.size, false, 0) let ctx = UIGraphicsGetCurrentContext()! let rectPath = UIBezierPath(rect: clipRect) rectPath.addClip() imageView.layer.renderInContext(ctx) let image = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() // 再次开启位图上下文,绘制裁剪后的图片 UIGraphicsBeginImageContextWithOptions(clipRect.size, false, 0) image.drawAtPoint(CGPoint(x: -startPoint.x, y: -startPoint.y)) let newImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() imageView.image = newImage clipRectView.frame = CGRectZero } } /// 裁剪区域黑色透明遮罩 private lazy var clipRectView: UIView = { let clipRectView = UIView() clipRectView.backgroundColor = UIColor(white: 0, alpha: 0.5) self.view.addSubview(clipRectView) return clipRectView }()
最终newImage就是我们需要的图片,demo下载:clipPhotoDemo