SwiftUIでのタップ処理の実装と応用

# はじめに
この記事は[and factory.inc Advent Calendar 2023](https://qiita.com/advent-calendar/2023/andfactory) 21日目の記事です。
昨日は @twumo さんの[【JetpackCompose】TabRowのインジケーターをカスタマイズする](https://qiita.com/twumo/items/26aa023ab71ffc09e2b2)でした。

最近個人的に実務でSwiftUIに触れる機会が増えてきたので基本的なタップ周りの処理をまとめておこうと思います。

### 基本的なタップ処理
#### タップジェスチャ（onTapGesture）
最も基本的なタップジェスチャで、ビューがタップされたときのアクションを定義します。

```swift
import SwiftUI

struct TapGestureView: View {
    var body: some View {
        Text("タップしてください")
            .onTapGesture {
                print("タップされました")
            }
    }
}
```
#### ダブルタップジェスチャ
onTapGestureにcountパラメータを設定することで、ダブルタップなどの複数回のタップに対応できます。

```swift
import SwiftUI

struct DoubleTapGestureView: View {
    var body: some View {
        Text("ダブルタップしてください")
            .onTapGesture(count: 2) {
                print("ダブルタップされました")
            }
    }
}
```
### カスタムタップ処理
#### ロングプレスジェスチャ
LongPressGestureを使用して、長押しジェスチャを検出します。

```swift
import SwiftUI

struct LongPressGestureView: View {
    var body: some View {
        Text("長押ししてください")
            .onLongPressGesture {
                print("長押しされました")
            }
    }
}
```
#### ハイパーリンク（.link修飾子）
テキストやビューにハイパーリンクを追加し、タップすると外部ブラウザでURLが開きます。

```swift
import SwiftUI

struct LinkView: View {
    var body: some View {
        Text("外部ブラウザで開く")
            .foregroundColor(.blue)
            .link(destination: URL(string: "https://www.example.com")!)
    }
}
```
#### 合成されたジェスチャ（simultaneousGesture）
複数のジェスチャを同時に検出するために使用します。

```swift
import SwiftUI

struct SimultaneousGestureView: View {
    var body: some View {
        Text("タップまたは長押し")
            .onTapGesture {
                print("タップされました")
            }
            .simultaneousGesture(LongPressGesture().onEnded { _ in
                print("長押しされました")
            })
    }
}
```
### SwiftUIのタップ処理の応用例
#### コンビネーションジェスチャの使用
複数のジェスチャを組み合わせることで、より複雑なユーザーインタラクションを作成できます。以下は、ドラッグとタップの組み合わせの例です。
```swift
import SwiftUI

struct CombinationGestureView: View {
    @State private var dragOffset = CGSize.zero

    var body: some View {
        Rectangle()
            .fill(Color.blue)
            .frame(width: 100, height: 100)
            .offset(dragOffset)
            .gesture(
                DragGesture()
                    .onChanged { value in
                        self.dragOffset = value.translation
                    }
                    .simultaneously(with: TapGesture().onEnded {
                        print("Rectangle Tapped")
                    })
            )
    }
}
```

#### カスタムジェスチャの作成
SwiftUIではカスタムジェスチャも作成できます。これにより、特定のユーザーインタラクションパターンに合わせた動作を実装できます。以下は、独自のジェスチャを定義する例です。
```swift
import SwiftUI

struct CustomGestureView: View {
    var customTapGesture: some Gesture {
        TapGesture()
            .onEnded {
                print("Custom Gesture Tapped")
            }
    }

    var body: some View {
        Circle()
            .fill(Color.red)
            .frame(width: 100, height: 100)
            .gesture(customTapGesture)
    }
}
```

### UIKitと比較
**SwiftUIの長所**
- コード量が少なく、直感的。
- 状態の変化に応じて自動的にUIが更新される。

**SwiftUIの短所**
- カスタムジェスチャーの柔軟性が少ない場合がある。
- 特定の複雑なインタラクションの実装は難しいことがある。例えば、外部ブラウザを起動せずに、タップだけを検知するハイパーリンクのような機能を実装するのは、技術的な工夫を必要とすることがある。

## おわりに
SwiftUIは、基本的なジェスチャからカスタムジェスチャまで、幅広い範囲のタップ処理が簡単に実装できます。しかし、特定の複雑なシナリオや高度なカスタマイズが必要な場合は、UIKitとの統合が必要となります。`UIViewControllerRepresentable`などの機能を使い、SwiftUIとUIKitの長所を組み合わせることで、様々な動きを実現することが可能です。

この記事が、SwiftUIでのタップ処理の実装から応用までを学ぶ際の参考になれば幸いです。

明日の[Advent Calender](https://qiita.com/advent-calendar/2023/andfactory)もお楽しみに！