1. 简介
不搞哪些花里胡哨的,如何利用系统 API 快速实现刮刮乐。
2. 实战
思路:通过蒙版实现刮的效果。刮的过程,可以转换为一个画图的过程。
2.1 子类化 UIPanGestureRecognizer
class SNXPanGestureRecognizer: UIPanGestureRecognizer {
var touchBeganLocation: CGPoint?
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
super.touchesBegan(touches, with: event)
touchBeganLocation = touches.first?.location(in: view)
}
}
为什么要子类化 UIPanGestureRecognizer 呢?
因为 UIPanGestureRecognizer 在识别的时候有一个识别距离,如果从 UIGestureRecognizer.State.began 才开始记录,就会丢失掉一个关键点,导致用户觉得不是从自己开始刮的地方开始的。
2.2 刮刮乐视图
创建两层视图:
coverView
在底层,可以用纯色,或者喜欢的刮刮乐封面图片。imageView
在上层。因为后面会用透明的蒙层,所以会看不到里面的内容,视觉上就像只有刮刮乐封面一样。当我们绘制了图案后,图案部分会被展示出来,遮挡coverView
就像被刮出来了一样。
// 封面视图
let coverView: UIView = UIView()
coverView.backgroundColor = UIColor.gray
coverView.frame = CGRect(x: 0, y: 0, width: 350, height: 235)
coverView.center = view.center
view.addSubview(coverView)
// 一等奖图片
let imageView: UIImageView = UIImageView()
imageView.frame = coverView.frame
imageView.image = UIImage(named: "prize")
imageView.contentMode = .scaleAspectFill
view.addSubview(imageView)
蒙层的代码也很简单。配置好需要的参数后,为 imageView
添加上 mask 就行了。
// maskLayer 为 VC 的实例变量,后面还要在 action 回调里面使用
let maskLayer: CAShapeLayer = CAShapeLayer()
// 蒙层
maskLayer.frame = imageView.bounds
maskLayer.lineWidth = 8.0
maskLayer.lineCap = CAShapeLayerLineCap.round
maskLayer.lineJoin = CAShapeLayerLineJoin.round
maskLayer.strokeColor = UIColor.black.cgColor
maskLayer.fillColor = UIColor.clear.cgColor
imageView.layer.mask = maskLayer
手势我们加在了 coverView
上。
imageView
默认是不响应手势的,所以这里我加在了coverView
上。这里可以根据个人喜好,添加到自己喜欢的视图上。
// 手势
let panGes: SNXPanGestureRecognizer = SNXPanGestureRecognizer(target: self, action: #selector(panGesHandler(_:)))
coverView.addGestureRecognizer(panGes)
响应代码也很简单。
UIGestureRecognizer.State.began 时移动到用户开始的触摸点,然后 addLine(to:) 手势识别的坐标,就把用户开始的部分补上了。之后不断调用 addLine(to:) 即可完成绘制。最后再将 path
更新到 maskLayer
上就完成了。
// path 为 VC 的实例变量
let path: UIBezierPath = UIBezierPath()
@objc func panGesHandler(_ recognizer: SNXPanGestureRecognizer) {
switch recognizer.state {
case .began:
if let touchBeganLocation = recognizer.touchBeganLocation {
path.move(to: touchBeganLocation)
}
fallthrough
case .changed, .ended, .cancelled:
path.addLine(to: recognizer.location(in: recognizer.view))
default:
break
}
maskLayer.path = path.cgPath
}
2.3 完整代码
class ViewController: UIViewController {
let maskLayer: CAShapeLayer = CAShapeLayer()
let path: UIBezierPath = UIBezierPath()
override func viewDidLoad() {
super.viewDidLoad()
// 封面视图
let coverView: UIView = UIView()
coverView.backgroundColor = UIColor.gray
coverView.frame = CGRect(x: 0, y: 0, width: 350, height: 235)
coverView.center = view.center
view.addSubview(coverView)
// 一等奖图片
let imageView: UIImageView = UIImageView()
imageView.frame = coverView.frame
imageView.image = UIImage(named: "prize")
imageView.contentMode = .scaleAspectFill
view.addSubview(imageView)
// 蒙层
maskLayer.frame = imageView.bounds
maskLayer.lineWidth = 8.0
maskLayer.lineCap = CAShapeLayerLineCap.round
maskLayer.lineJoin = CAShapeLayerLineJoin.round
maskLayer.strokeColor = UIColor.black.cgColor
maskLayer.fillColor = UIColor.clear.cgColor
imageView.layer.mask = maskLayer
// 手势
let panGes: SNXPanGestureRecognizer = SNXPanGestureRecognizer(target: self, action: #selector(panGesHandler(_:)))
coverView.addGestureRecognizer(panGes)
}
@objc func panGesHandler(_ recognizer: SNXPanGestureRecognizer) {
switch recognizer.state {
case .began:
if let touchBeganLocation = recognizer.touchBeganLocation {
path.move(to: touchBeganLocation)
}
fallthrough
case .changed, .ended, .cancelled:
path.addLine(to: recognizer.location(in: recognizer.view))
default:
break
}
maskLayer.path = path.cgPath
}
}
如果觉得本文不错,给我点个赞吧~❤️