iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 7
0
Software Development

iOS Swift x Layout x Animation x Transition系列 第 7

Scratch Card - 刮刮樂的做法

https://ithelp.ithome.com.tw/upload/images/20171226/201073292nYcyOzlRd.png
以前住在上海,冬天冷的時候常常到 0~2度之間,還記得小時候會在窗戶上吐氣弄出一團霧,然後在上面畫個笑臉,透過笑臉可以看到外面的景色。

這個效果和刮刮卡很像,在可見物上面在塗一層東西,當去掉這層東西後,就可以看到底下的內容了,這次就在 iOS 做一個看看吧。


Scratch Card

Scratch Card
就如同上面的動畫,要做到:

  • 通過手指在畫面上移動的時候,可以擦拭藍色漸層的畫面,而顯示出背後的人像圖片。
  • 而當我們點最上方的橡皮擦時,可以還原。

Mask

要實現上面的方法可能很多,但這次要利用 mask 來實現這個功能。

因為有了 mask 的,要顯示某一張圖的部分內容就顯得特別的方便,尤其是不規則的形狀。

比如下面的圖,我們的 mask 是原型又或者是星星,這張圖片圖片就能呈現出不同的形狀。

https://ithelp.ithome.com.tw/upload/images/20171226/20107329Qhus9qlSZ0.png

有了 mask 我們也能實現我們要的刮刮卡功能了

  • cover 就是我們的遮擋層
  • content 是去掉遮擋層後能看到的內容
  • mask 是 content 的 mask 也就是只有 mask 和 content 重疊的部分我們才能夠看到內容。

https://ithelp.ithome.com.tw/upload/images/20171226/201073290NIsRVpY9m.png

View Hierarchy

我們在畫面上依次放入 cover / content / mask
https://ithelp.ithome.com.tw/upload/images/20171226/201073294ZiytErxrq.png

這時候會發現畫面上只會看到一篇橘色。

而當 content 設定 mask 以後,就變成只能看見那張圖片,因為 mask 和圖片完整的重疊到了。

但如果我們將 mask 的背景色改為 clear 以後呢?就會變成只看得見藍色漸層的畫面。

重點來了,因為 mask 上有多少內容,就能看到多少圖片,而圖片是在 cover 之上的,所以圖片能顯示多少,就會改掉多少藍色漸層的畫面,

從而達到了視覺上的「刮掉藍色漸層」的效果。


SKScratchCardView

建立一個繼承於 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()
}

當使用者在 SKScratchCardView 滑動的時候,我們將滑動的事件交給 Mask (SKScratchCardMaskView)

SKScratchCardMaskView

當拿到 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()
}

參考


上一篇
Parallax Scrolling - 橫向捲動視差
下一篇
Flip Card - 翻轉圖片
系列文
iOS Swift x Layout x Animation x Transition30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言