SwiftUIでMasterDetailを作成する

# はじめに
SwiftUIでiPad（横向き固定）のMaster Detail（SplitView）を作成するのに、かなり手こずった。
記述することは多くないし結果としては簡単な内容なのだが、ハマるとなかなか目的を果たせないので、記事として残しておく。

# マスタ（左側テーブルビュー）の表示

```swift
struct MasterDetail1View: View {
    @State private var fruits = ["Apple", "Banana", "Cherry", "Dragon Fruit"]
    @State private var selectedFruit = ""

    var body: some View {
        NavigationView {
            List {
                ForEach(fruits, id: \.self) { fruit in
                    Text(fruit)
                        .onTapGesture {
                            self.selectedFruit = fruit
                    }
                }
                .onAppear {
                    self.selectedFruit = "Apple"
                }
            }
            // 常に2カラムで良いのでナビゲーションバーは非表示
            .navigationBarHidden(true)
        }
   }
}
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/37598/190583e7-e421-2e8d-a5d5-ebcf12ecd214.png)



デフォルトだと左上端ににマスター（左テーブルビュー）表示／非表示のアイコンが表示される。常にマスタ（左側テーブルビュー）表示で固定したいので`.navigationBarHidden`を`true`にする。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/37598/74a67e46-a72b-19e8-3928-43240301bbd7.png)


# 選択セルに色を付ける

```swift
struct MasterDetail2View: View {
    @State private var fruits = ["Apple", "Banana", "Cherry", "Dragon Fruit"]
    @State private var selectedFruit = ""

    var body: some View {
        NavigationView {
            List {
                ForEach(fruits, id: \.self) { fruit in
                    // テキスト以外の部分（セル全体）も含めてひとつの表示領域にする。
                    HStack{
                        Text(fruit)
                        Spacer()
                    }
                    // 形状を矩形全体とする。
                    .contentShape(Rectangle())
                    .onTapGesture {
                        self.selectedFruit = fruit
                    }
                    // 選択セル色を付ける
                    .listRowBackground(self.selectedFruit == fruit ? Color.gray.opacity(0.25) : Color.clear)
                }
                .onAppear {
                    self.selectedFruit = "Apple"
                }
            }
            .navigationBarHidden(true)
        }
    }
}
```
タップイベントをを取得するために`.onTapGesture`を記述。`.listRowBackground`で色を設定。
`Text`のみだとテキストの部分でしかタップイベントを取得できない。`Spacer()`とともに`HStack`で包み` .contentShape(Rectangle())`を記述することでセル全体でタップイベントが取得できる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/37598/e9ed4ebe-6741-30ae-ab93-3e57b52bd08c.png)

# Detail表示

```swift
struct MasterDetail3View: View {
    @State private var fruits = ["Apple", "Banana", "Cherry", "Dragon Fruit"]
    @State private var selectedFruit = ""

    var body: some View {
        NavigationView {
            List {
                ForEach(fruits, id: \.self) { fruit in
                    HStack{
                        Text(fruit)
                        Spacer()
                    }
                    .contentShape(Rectangle())
                    .onTapGesture {
                        self.selectedFruit = fruit
                    }
                    .listRowBackground(self.selectedFruit == fruit ? Color.gray.opacity(0.25) : Color.clear)
                }
                .onAppear {
                    self.selectedFruit = "Apple"
                }
            }
            .navigationBarHidden(true)
            // セルの表示形式を横幅いっぱいにする
            .listStyle(PlainListStyle())

            // Listと同じ階層にDetailを配置する
            Detail(title: selectedFruit)
        }
    }
}

struct Detail: View {
    let title: String
    var body: some View {
        Text("\(self.title)").font(.system(size: 50)).frame(maxWidth: .infinity)
    }
}
```
Detail用の`View`を`List`の下に記述するだけで右側に`Detail`を表示することはできるが、それだけだとマスタ（左側テーブルビュー）の表示にパディングができる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/37598/56ae49d7-82cd-2f86-3277-b1f4ad48bf2c.png)
`.listStyle(PlainListStyle())`を記述することで、横幅いっぱいのマスタ（左側テーブルビュー）として表示できる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/37598/5c83143e-5bcd-d844-6f45-4f9257b8ee0f.png)


[ソースコード](https://github.com/moccow/MasterDetailOnSwiftUI)