はじめに
はじめまして、Hridoy Chandra Das(リド)(@ihridoydas)です。SwiftUI で、Google Pay 特典のスクラッチ カードのようなスクラッチ カードを作成します。
スクラッチカードは競技用に設計されたカードで、多くの場合、PIN を隠すために薄いカードストックまたはプラスチックで作られており、1 つまたは複数の領域に隠された情報が含まれており、不透明なカバーを剥がすことで明らかになります。
ユーザーはデバイスの画面上でカードを仮想的に「スクラッチ」「指で擦る」し、その下に隠されたコンテンツを明らかにすることができます。 このコンテンツは、クーポン コード、割引、その他の特典を紹介するためによく使用されます。
SwiftUI の.mask Modifier(Instance Property)を使用すると、あるビューを別のビューでマスクできます。 簡単な例から始めましょう。
Mask
maskは、レンダリング システムが指定されたビューにアルファを適用するビュー。こちらで.mask Modifierを使用して二つ方法でスクラッチビュー作成のついて共有したいと思います。(Path、Canvas)
方法:1(Path)
PathView.swift
// MARK: Scratchable overlay view
RoundedRectangle(cornerRadius: 20)
.frame(width: 250, height: 250)
Image("bg", bundle: nil)
.resizable()
.frame(width: 250, height: 250)
.cornerRadius(20)
// MARK: Hidden content view
Image("bg", bundle: nil)
.resizable()
.frame(width: 250, height: 250)
.cornerRadius(20)
.overlay {
Image("won", bundle: nil)
.resizable()
.scaledToFit()
.frame(width: 200)
}
.mask(
Path { path in
path.addLines(currentPathState)
}.stroke(style: StrokeStyle(lineWidth: 50, lineCap: .round, lineJoin: .round))
)
.gesture(
DragGesture(minimumDistance: 0, coordinateSpace: .local)
.onChanged({ value in
currentPathState.append(value.location)
})
)
完全な作成ビュー(Path) : ScratchCardView
ScratchCardView.swift
import SwiftUI
struct ScratchCardView: View {
@State var currentPathState = [CGPoint]()
//navigationBarTitle
init() {
UINavigationBar.appearance().titleTextAttributes = [.font : UIFont(name: "Georgia-Bold", size: 20)!]
}
var body: some View {
NavigationView {
VStack {
ZStack{
// MARK: Scratchable overlay view
RoundedRectangle(cornerRadius: 20)
.frame(width: 250, height: 250)
Image("bg", bundle: nil)
.resizable()
.frame(width: 250, height: 250)
.cornerRadius(20)
// MARK: Hidden content view
Image("bg", bundle: nil)
.resizable()
.frame(width: 250, height: 250)
.cornerRadius(20)
.overlay {
Image("won", bundle: nil)
.resizable()
.scaledToFit()
.frame(width: 200)
}
.mask(
Path { path in
path.addLines(currentPathState)
}.stroke(style: StrokeStyle(lineWidth: 50, lineCap: .round, lineJoin: .round))
)
.gesture(
DragGesture(minimumDistance: 0, coordinateSpace: .local)
.onChanged({ value in
currentPathState.append(value.location)
})
)
}
}
.navigationBarTitle (Text("Scratch Card"), displayMode: .automatic)
.toolbar{
ToolbarItemGroup(placement: .navigationBarTrailing){
Button{
currentPathState.removeAll()
} label: {
Label("Clear",systemImage: "clear.fill")
}
}
}
.accentColor(.red)
}
}
}
struct ScratchCardViewPreview: PreviewProvider {
static var previews: some View {
ScratchCardView()
}
}
方法:2(Canvas)
Canvas.swift
// MARK: Scratchable overlay view
RoundedRectangle(cornerRadius: 20)
.frame(width: 250, height: 250)
Image("bg", bundle: nil)
.resizable()
.frame(width: 250, height: 250)
.cornerRadius(20)
// MARK: Hidden content view
Image("bg", bundle: nil)
.resizable()
.frame(width: 250, height: 250)
.cornerRadius(20)
.overlay {
Image("won", bundle: nil)
.resizable()
.scaledToFit()
.frame(width: 200)
}
// Mask the Canvas
.mask(
Canvas { context, _ in
for line in lines {
var path = Path()
path.addLines(line.points)
context.stroke(path,
with: .color(.white),
style: StrokeStyle(lineWidth:line.lineWidth,
lineCap: .round,
lineJoin: .round)
)}
}
)
// User Can gesture and change the point value
.gesture(
DragGesture(minimumDistance: 0, coordinateSpace: .local)
.onChanged({ value in
let newPoint = value.location
currentLine.points.append(newPoint)
lines.append(currentLine)
}))
完全な作成ビュー(Canvas) : ScratchCardCanvasView
ScratchCardCanvasView.swift
import SwiftUI
struct Line {
var points = [CGPoint]()
var lineWidth: Double = 50.0
}
struct ScratchCardCanvasView: View {
@State private var currentLine = Line()
@State private var lines = [Line]()
init() {
UINavigationBar.appearance().titleTextAttributes = [.font : UIFont(name: "Georgia-Bold", size: 20)!]
}
var body: some View {
NavigationView {
VStack {
ZStack {
// MARK: Scratchable overlay view
RoundedRectangle(cornerRadius: 20)
.frame(width: 250, height: 250)
Image("bg", bundle: nil)
.resizable()
.frame(width: 250, height: 250)
.cornerRadius(20)
// MARK: Hidden content view
Image("bg", bundle: nil)
.resizable()
.frame(width: 250, height: 250)
.cornerRadius(20)
.overlay {
Image("won", bundle: nil)
.resizable()
.scaledToFit()
.frame(width: 200)
}
.mask(
Canvas { context, _ in
for line in lines {
var path = Path()
path.addLines(line.points)
context.stroke(path,
with: .color(.white),
style: StrokeStyle(lineWidth: line.lineWidth,
lineCap: .round,
lineJoin: .round)
)
}
}
)
.gesture(
DragGesture(minimumDistance: 0, coordinateSpace: .local)
.onChanged({ value in
let newPoint = value.location
currentLine.points.append(newPoint)
lines.append(currentLine)
})
)
}
}
.navigationBarTitle (Text("Scratch Card Canvas"), displayMode: .automatic)
.toolbar{
ToolbarItemGroup(placement: .navigationBarTrailing){
Button{
currentLine.points.removeAll()
lines.removeAll()
} label: {
Label("Clear",systemImage: "clear.fill")
}
}
}
.accentColor(.red)
}
}
}
struct ScratchCardCanvasViewPreview: PreviewProvider {
static var previews: some View {
ScratchCardCanvasView()
}
}
結果:
以上、最後までお読みいただきありがとうございました。