> For the complete documentation index, see [llms.txt](https://lochiwei.gitbook.io/ios/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://lochiwei.gitbook.io/ios/master/todo/tutorials/ui-design/gestures.md).

# 設定手勢

學習如何控制手勢 (gestures) 的觸控事件 (events)。本節新增了「拖曳」主卡片時，會產生的行為：「拖曳主卡時，所有卡片都會跟著跑；拖曳停止時，所有卡片彈回原來的位置」。

## 重點

* 用 view.**onTapGesture**() 來設定「點擊」的觸控事件。
* 用 view.**gesture**() 來設定一般的觸控事件 (如：drag, pinch, long press 等)。
* 用 drag.**onChanged**() 來控制「拖曳發生時」的行為，用 drag.**onEnded**() 來控制「拖曳結束時」的行為。
* 拖曳時，系統會提供一個 **DragGesture.Value** (struct) 的值供我們使用，其中的 **translation** 屬性是「拖曳的平移量」 (類別為 **CGSize**)。（🤔 謎之音：真不知 Apple 的工程師在想什麼，平移量是一個「向量」，為什麼不是 `CGPoint` 或 `CGVector` 呢？）

{% tabs %}
{% tab title="main" %}

```swift
/*
 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())
```

{% endtab %}

{% tab title="參考" %}
{% embed url="<https://designcode.io/swiftui-gestures-and-events>" %}

{% endtab %}
{% endtabs %}
