設定手勢
學習如何控制手勢 (gestures) 的觸控事件 (events)。本節新增了「拖曳」主卡片時,會產生的行為:「拖曳主卡時,所有卡片都會跟著跑;拖曳停止時,所有卡片彈回原來的位置」。
重點
用 view.onTapGesture() 來設定「點擊」的觸控事件。
用 view.gesture() 來設定一般的觸控事件 (如:drag, pinch, long press 等)。
用 drag.onChanged() 來控制「拖曳發生時」的行為,用 drag.onEnded() 來控制「拖曳結束時」的行為。
拖曳時,系統會提供一個 DragGesture.Value (struct) 的值供我們使用,其中的 translation 屬性是「拖曳的平移量」 (類別為 CGSize)。(🤔 謎之音:真不知 Apple 的工程師在想什麼,平移量是一個「向量」,為什麼不是
CGPoint
或CGVector
呢?)
/*
Design+Code - SwiftUI for iOS 13
https://designcode.io/swiftui-gestures-and-events
*/
import SwiftUI
import PlaygroundSupport
struct ContentView: View {
// view states
@State private var show = false // ⭐️ 控制要不要「展開卡片」
@State private var translation = CGSize.zero // ⭐️ 控制拖曳主卡時的「位移量」
// view body
var body: some View {
ZStack {
// 標題
TitleView()
.blur(radius: show ? 20 : 0)
.animation(.default)
// 底卡一
BackCard(.purple)
.scaleEffect(0.9)
.offset(x: 0, y: show ? -400 : -40) // ⚠️ 注意:
.offset(translation) // 平移(offset)愛設定幾次都可以!
.rotationEffect(.degrees(show ? 0 : 10))
.rotation3DEffect(.degrees(10), axis: (x: 1, y: 0, z: 0))
.blendMode(.hardLight)
.animation(.easeInOut(duration: 0.5))
// 底卡二
BackCard(.pink)
.scaleEffect(0.95)
.offset(x: 0, y: show ? -200 : -20)
.offset(translation)
.rotationEffect(.degrees(show ? 0 : 5))
.rotation3DEffect(.degrees(5), axis: (x: 1, y: 0, z: 0))
.blendMode(.hardLight)
.animation(.easeInOut(duration: 0.3))
// 主卡
Card()
// 1. 設定平移量
.offset(translation)
.blendMode(.hardLight)
// 2. 設定彈回動畫
.animation(.spring(response: 0.3, dampingFraction: 0.6, blendDuration: 0))
// ⭐️ 點擊主卡時,展開或收合卡片。
.onTapGesture { self.show.toggle() }
// ⭐️ 拖曳主卡時,主卡會跟著跑。 (1.2.3.)
// 停止拖曳時,主卡會彈回原位。 (1.2.4.)
.gesture(
DragGesture().onChanged{ value in // DragGesture.Value
// 拖曳的「位移量」竟然是個 CGSize,豈不怪哉 🤔⁉️
self.translation = value.translation // 3. 紀錄平移量
self.show = true
}.onEnded{ value in
self.translation = .zero // 4. 清除平移量
self.show = false
}
)
BottomCard()
.blur(radius: show ? 20 : 0)
.animation(.default)
} // ZStack (container)
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color.white)
}
}
PlaygroundPage.current.setLiveView(ContentView())
Last updated