以前住在上海,冬天冷的時候常常到 0~2度之間,還記得小時候會在窗戶上吐氣弄出一團霧,然後在上面畫個笑臉,透過笑臉可以看到外面的景色。
這個效果和刮刮卡很像,在可見物上面在塗一層東西,當去掉這層東西後,就可以看到底下的內容了,這次就在 iOS 做一個看看吧。
就如同上面的動畫,要做到:
要實現上面的方法可能很多,但這次要利用 mask 來實現這個功能。
因為有了 mask 的,要顯示某一張圖的部分內容就顯得特別的方便,尤其是不規則的形狀。
比如下面的圖,我們的 mask 是原型又或者是星星,這張圖片圖片就能呈現出不同的形狀。
有了 mask 我們也能實現我們要的刮刮卡功能了
我們在畫面上依次放入 cover / content / mask
這時候會發現畫面上只會看到一篇橘色。
而當 content 設定 mask 以後,就變成只能看見那張圖片,因為 mask 和圖片完整的重疊到了。
但如果我們將 mask 的背景色改為 clear 以後呢?就會變成只看得見藍色漸層的畫面。
重點來了,因為 mask 上有多少內容,就能看到多少圖片,而圖片是在 cover 之上的,所以圖片能顯示多少,就會改掉多少藍色漸層的畫面,
從而達到了視覺上的「刮掉藍色漸層」的效果。
建立一個繼承於 UIView 的 SKScratchCardView 對外提供設定 contentView 和 coverView 的方法。
拿到 contentView & coverView 以後,設定 frame 並且設定 UIPanGesture.
func setupWith(coverView:UIView, contentView:UIView) {
// cover view
self.coverView = coverView
// content view
self.contentView = contentView
// maskView
self.contentMaskView.frame = coverView.frame
self.contentMaskView.backgroundColor = UIColor.clear
self.contentMaskView.strokeWidth = 40.0
// addSubviews
addSubviewFullscreen(self.coverView)
addSubviewFullscreen(self.contentView)
addSubviewFullscreen(self.contentMaskView)
// set mask
self.contentView.mask = self.contentMaskView
// add gesture
setupPanGesture()
}
當拿到 panGestureRecognizer 的時候,我們開始在 mask 上面畫線。
@objc func panGestureRecognizer(_ recognizer:UIPanGestureRecognizer) {
let location = recognizer.location(in: self)
switch recognizer.state {
case .began:
beginPath(at: location)
case .changed:
addLine(to: location)
default:
closePath()
}
}
通過 CGMutablePath 來一次完整的畫畫過程(手指按下到拿起)
並將每一次畫好的內容都存在 paths 中
fileprivate var paths: [CGMutablePath] = []
public func beginPath(at point: CGPoint) {
currentPath = CGMutablePath()
currentPath?.move(to: point)
setNeedsDisplay()
}
public func addLine(to point: CGPoint) {
currentPath?.addLine(to: point)
setNeedsDisplay()
}
public func closePath() {
if let currentPath = currentPath {
paths.append(currentPath)
}
currentPath = nil
setNeedsDisplay()
}
畫的過程或結束時,通過 setNeedsDisplay 方法來觸發 Draw
draw 方法會將已經畫過 ( paths ) 和最後畫的 (contentPath) 的內容畫在畫面上。
override func draw(_ rect: CGRect) {
let context = UIGraphicsGetCurrentContext()
context?.setStrokeColor(strokeColor.cgColor)
context?.setLineWidth(strokeWidth)
context?.setLineCap(.round)
for path in paths + [currentPath].flatMap({$0}) {
context?.addPath(path)
context?.strokePath()
}
}
如果要清除畫好的內容,也只需要清空 paths 然後在通過 setNeedsDisplay 觸發 draw 方法達到畫面上沒內容,從而看不到人像圖片。
public func clearCanvas() {
paths.removeAll()
setNeedsDisplay()
}