有些时候我们需要处理拍照后的图片截取操作,比如在上传用户头像的时候,用户上传的图片可能是很长的一张图片,但这样最后显示的头像就不会太完美。我们需要在用户上传头像的时候,有一个可以截取图片的功能。
需求分析
从上图直观看出,随着用户的触摸移动,会创建一个透明遮罩来表示需要截取的区域,然后截取了区域里的图片。因为我这里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
}()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
@IBOutletweakvarimageView:UIImageView!
privatevarstartPoint=CGPointZero
overridefuncviewDidLoad(){
super.viewDidLoad()
// 给当前控制器view添加一个pan手势
letpanGesture=UIPanGestureRecognizer(target:self,action:#selector(didPan(_:)))
view.addGestureRecognizer(panGesture)
}
@objcprivatefuncdidPan(pan:UIPanGestureRecognizer){
letendPoint=pan.locationInView(view)
letclipRect=CGRect(origin:startPoint,size:CGSize(width:endPoint.x-startPoint.x,height:endPoint.y-startPoint.y))
ifpan.state==.Began{
startPoint=pan.locationInView(view)
}elseifpan.state==.Changed{
clipRectView.frame=clipRect
}elseifpan.state==.Ended{
// 开启位图上下文,并裁剪指定区域
UIGraphicsBeginImageContextWithOptions(imageView.bounds.size,false,0)
letctx=UIGraphicsGetCurrentContext()!
letrectPath=UIBezierPath(rect:clipRect)
rectPath.addClip()
imageView.layer.renderInContext(ctx)
letimage=UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
// 再次开启位图上下文,绘制裁剪后的图片
UIGraphicsBeginImageContextWithOptions(clipRect.size,false,0)
image.drawAtPoint(CGPoint(x:-startPoint.x,y:-startPoint.y))
letnewImage=UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
imageView.image=newImage
clipRectView.frame=CGRectZero
}
}
/// 裁剪区域黑色透明遮罩
privatelazyvarclipRectView:UIView={
letclipRectView=UIView()
clipRectView.backgroundColor=UIColor(white:0,alpha:0.5)
self.view.addSubview(clipRectView)
returnclipRectView
}()
最终newImage就是我们需要的图片,demo下载:clipPhotoDemo