【SwiftUI】 Listを使ってUITableViewをなくす！

#やりたいこと
・Listの背景色をつける
・削除可能にする
・タップ時のアクションを作る
・ContextMenu で長押し/3D Touch時のアクションを作る



#Listを作成
🐶、🐱、🐊の3つを表示する高さが 50 のList を作成します
`id: \.self ` となっているのは[この記事](https://qiita.com/titanium/items/d0e636fe6a17210605a8)で紹介しています

```swift
struct someView: View {
    @State var animals: [String] = ["🐶", "🐱", "🐊"]
    var body: some View {
        List {
            ForEach(animals, id: \.self) { animal in
                HStack {
                    Text(animal)
                        .padding(.all)
                    Spacer()
                }
            }
            .frame(height: 50)
        }
    }
}       
```
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/409695/a52199f8-82cd-fa04-28db-9b185f0757e8.png" width="20%">

#Listに背景色をつける
背景色は`.listRowBackgruond`で指定します

```swift
struct someView: View {
    @State var animals: [String] = ["🐶", "🐱", "🐊"]
    var body: some View {
        List {
            ForEach(animals, id: \.self) { animal in
                HStack {
                    Text(animal)
                        .padding(.all)
                    Spacer()
                }
            }
            .frame(height: 50)
            // 背景色設定
            .listRowBackground(Color.gray)
        }
    }
}   
```

この時、Viewの背景色も合わせて変えたいという時はイニシャライザでTableViewの背景色を透明にしてZStack等を使うと良いです

```swift
struct someView: View {
    @State var animals: [String] = ["🐶", "🐱", "🐊"]
    init() {
        UITableView.appearance().backgroundColor = .clear
    }
    var body: some View {
        NavigationView {
            ZStack {
                Color(.red).edgesIgnoringSafeArea(.all)
                List {
                    ForEach(animals, id: \.self) { animal in
                        HStack {
                            Text(animal)
                                .padding(.all)
                            Spacer()
                        }
                        
                    }
                    .frame(height: 50)
                    .listRowBackground(Color.blue)
                }.navigationTitle("Animals")
            }
        }
    }
}
```

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/409695/8ebdc935-bee5-e36b-fa02-c14fc20daab5.png" width="20%">

#削除メソッド
`ForEach`メソッドの最後にonDeleteメソッドをつけると左へスワイプできるようになります
この時、`offsets(IndexSet)`が引数として与えられます

```swift
struct someView: View {
    @State var animals: [String] = ["🐶", "🐱", "🐊"]
    var body: some View {
        List {
            ForEach(animals, id: \.self) { animal in
                HStack {
                    Text(animal)
                        .padding(.all)
                    Spacer()
                }
            }
            // 削除メソッド
            .onDelete(perform: delete)
            .frame(height: 50)
            .listRowBackground(Color.gray)
        }
    }
    
    func delete(at offsets: IndexSet) {
        animals.remove(atOffsets: offsets)
    }
}
```
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/409695/ef50fd0c-26a2-9259-a86e-f378936f6cb5.png" width="20%">

#タップを可能にする
`HStack`に対して、`.onTapGesture`を設定します
この時、このままだと`Text()`をタップしないとタップ判定されないようになってしまうので、`contentShape`を指定することにより、タップ領域を広げリストのコンテンツをタップすると判定されるようにします

```swift
struct someView: View {
    @State var animals: [String] = ["🐶", "🐱", "🐊"]
    var body: some View {
        List {
            ForEach(animals, id: \.self) { animal in
                HStack {
                    Text(animal)
                        .padding(.all)
                    Spacer()
                }
                // タップ判定領域を指定
                .contentShape(RoundedRectangle(cornerRadius: 5))
                // タップした時にtapActionを実行
                .onTapGesture {
                    tapAction(animal)
                }
            }
            .onDelete(perform: delete)
            .frame(height: 50)
            .listRowBackground(Color.gray)
        }
    }
    
    func tapAction(_ selection: String) {
        animals.append(selection)
    }
    
    func delete(at offsets: IndexSet) {
        animals.remove(atOffsets: offsets)
    }
}
```
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/409695/f08a2bb2-b6cd-17aa-962b-9e9525f799b2.png" width="20%">


#長押しContextMenu
`HStack`に対して`.contextMenu`を設定すると長押しした際にメニューが表示されるようになります
ContextMenuの中にMenuを入れ子のようにすることもできます

```swift
struct someView: View {
    @State var animals: [String] = ["🐶", "🐱", "🐊"]
    var body: some View {
        List {
            ForEach(animals, id: \.self) { animal in
                HStack {
                    Text(animal)
                        .padding(.all)
                    Spacer()
                }
                .contentShape(RoundedRectangle(cornerRadius: 5))
                .onTapGesture {
                    tapAction(animal)
                }
                // 長押しした時のアクション
                .contextMenu(ContextMenu(menuItems: {
                    Menu("More") {
                        Button(action: {
                            tapAction(animal)
                        }) {
                            Text("Add")
                            Image(systemName: "plus")
                        }
                    }
                    Text("Like")
                }))
            }
            .onDelete(perform: delete)
            .frame(height: 50)
            .listRowBackground(Color.gray)
        }
    }
    
    func tapAction(_ selection: String) {
        animals.append(selection)
    }
    
    func delete(at offsets: IndexSet) {
        animals.remove(atOffsets: offsets)
    }
}
```

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/409695/49f1915e-878e-beaa-0de4-44e42a8c98d6.png" width="20%">