SwiftUIでList内のViewに複数の遷移先を持つ

iOSアプリを開発する上で、UIKitのUITableViewCellでタップしたUIViewによって遷移先を変えたいことは、よくあると思います。
同じことをSwiftUIの `List` と `NavigationView` で実現しようとした際に試したことを備忘録として残しておきます。
環境はXcode11.0 (11A420a)、iOS13.0です。

## `NavigationLink` を2つ指定した場合

```swift
import SwiftUI

struct ContentView: View {
    var body: some View {
        NavigationView {
            List {
                Row(city: "Twentynine Palms", state: "California")
                Row(city: "Port Alsworth", state: "Alaska")
                Row(city: "Skagway", state: "Alaska")
            }
        }
    }
}

struct Row: View {
    var city: String
    var state: String
    var body: some View {
        VStack(alignment: .leading, spacing: 4) {
            NavigationLink(city, destination: Text(city))
            NavigationLink(state, destination: Text(state))
        }
    }
}

```
Pushの遷移を行う場合は `NavigationLink` 使用するので、素直に実装すると、このようになるかと思います。
これをビルドすると以下のような挙動になりました。

![Oct-01-2019 23-29-42.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151036/0dd41f49-7d6b-e780-cef8-07ca6be9762e.gif)

1回のタップで遷移を2回おこなっていて、意図した挙動になっていません。（また表示自体も `disclosureIndicator` が2つ表示されてしまっています）

## `Button` を2つ配置して `NavigationLink.init(destination:isActive:label:)` を使用した場合
`NavigationLink` のインターフェイスを調べていると`NavigationLink.init(destination:isActive:label:)` というメソッドが定義されています。
ドキュメントには定義以上の情報がないですが、利用できそうです。

```swift

import SwiftUI

struct ContentView: View {
    var body: some View {
        NavigationView {
            List {
                Row(city: "Twentynine Palms", state: "California")
                Row(city: "Port Alsworth", state: "Alaska")
                Row(city: "Skagway", state: "Alaska")
            }
        }
    }
}

struct Row: View {
    var city: String
    var state: String
    @State var buttonDidTap = (false, "")
    var body: some View {
        VStack(alignment: .leading, spacing: 4) {
            Button(city) {
                self.buttonDidTap = (true, self.city)
            }
            Button(state) {
                self.buttonDidTap = (true, self.state)
            }
            NavigationLink(destination: Text(buttonDidTap.1), isActive: $buttonDidTap.0) {
                EmptyView()
            }
        }
    }
}
```
実装は上記のように各ボタンのアクションを `NavigationLink` にバインドしています。

![Oct-02-2019 00-08-27.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151036/ea894dc3-9e34-e924-a244-09000fb468ea.gif)

一見上手く遷移できているようですが、どのボタンをタップしても2番目にあるボタンの遷移先になってしまっています。
各ボタンのアクションにブレイクポイントを貼って、もう少し挙動を確認してみます。

![Oct-02-2019 00-16-05.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151036/8e835364-b5ae-a52a-0774-15fe4cd4cb90.gif)

ボタンを1つだけタップしているはずですが、各ボタンのアクションが実行されてしまっています。
そのため最後のボタンイベントが優先されてしまい、このような挙動になってしまっているようです。

## `onTapGesture(count:perform:)` を使用した場合

`View.onTapGesture(count:perform:)` という、これまでに試した方法よりも抽象度の低いメソッドがあるようです。これを使用して試してみます。

```swift

import SwiftUI

struct ContentView: View {
    var body: some View {
        NavigationView {
            List {
                Row(city: "Twentynine Palms", state: "California")
                Row(city: "Port Alsworth", state: "Alaska")
                Row(city: "Skagway", state: "Alaska")
            }
        }
    }
}

struct Row: View {
    var city: String
    var state: String
    @State var buttonDidTap = (false, "")
    var body: some View {
        VStack(alignment: .leading, spacing: 4) {
            Text(city)
                .onTapGesture {
                    self.buttonDidTap = (true, self.city)
                }
            Text(state)
                .onTapGesture {
                    self.buttonDidTap = (true, self.state)
                }
            NavigationLink.init(destination: Text(buttonDidTap.1), isActive: $buttonDidTap.0) {
                EmptyView()
            }
        }
    }
}

```

![Oct-08-2019 23-17-13.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151036/406ad7af-3288-4283-b559-7cba804df713.gif)

上記が実行した際のキャプチャです。各 `Text` をタップした際に別画面に遷移することができています。

## 最後に

上記のように、`onTapGesture(count:perform:)` を使用した場合は実現できました。
他にも良い方法はありそうですね。もし知っている方がいれば教えてほしいです。

## 参考資料
https://qiita.com/noppefoxwolf/items/33a1695ed5daa3230f3b
https://developer.apple.com/documentation/swiftui/navigationlink/3364630-init