【初心者】Swift UIを勉強する その⑩ ーーー disabledとDispatchQueueアニメーションをもっと自由に

# はじめに

前回は選択されたCardをタップしたら、リスト一覧を出せるようにしました。
しかし、選択されたcoursesは固定のままで、今回はそれぞれのCardをそれぞれのリストを出せるようにします。

完成品↓
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1065841/d1198f02-dbd8-00ae-d969-536cecf01ae1.gif" width=40%>　　



# 目次
<!-- タイトルとアンカー名を編集 -->
1. [disabledとDispatchQueue](#disabledとDispatchQueue)
2. [まとめ](#まとめ)
3. [参考文献](#reference)

<!-- 各チャプター -->
<a id="#disabledとDispatchQueue"></a>
# disabledとDispatchQueue
・まずは`Course.swift`Courseを新たに作った`Model`Groupに移動します。([MVVM](https://qiita.com/am10/items/b35355df807105600f51))
・現状では、`show`がtrueかflaseかを条件としてCardの状態を管理しています。
 全てのCardは自分が持つリストを認識させるように、まずは`seletedItem`を作ります。
 デフォルトでnil([Optional](https://qiita.com/koher/items/c6f446bad54442a28bf4))をつめて、Cardがまだ選択されていない状態を意味します。

```swift:CoursesView.swift

@State var show = false
@Namespace var namespace
@State var selectedItem: Course? = nil

```


・タップジェスチャーはまだループの外にあるので、Cardの中に移動します。
 また、どのCardをタップしたのかをわかってもらうために、クロージャの`item`を`selectedItem`につめていきます。

```swift:CoursesView.swift

ForEach(courses) { item in
    CourseItem(course: item)
        .matchedGeometryEffect(
               id: item.id, in: namespace, isSource: !show
        )
           .frame(width: 335, height: 250)
           .onTapGesture {
                  withAnimation(.spring()) {
                         show.toggle()
                         selectedItem = item
                   }
            }
}

```



・Card中のListは↑上から`selectedItem`をもらって、自分専属のリストゲットできるようになりました。
・ただ、今はCardを開くことしかできなくて、閉じる場合はCardと同じく、Listも`onTapGesture()`を使います。また、Cardを閉じることによって、`selectedItem`もいらなくなるので、nilをつめていきます。

```swift:CoursesView.swift

if selectedItem != nil {
     ScrollView {
          CourseItem(course: selectedItem!)
               .matchedGeometryEffect(
                    id: selectedItem!.id, in: namespace
                )
               .frame(height: 300)
               .onTapGesture {
                    withAnimation(.spring()) {
                          show.toggle()
                          selectedItem = nil
                     }
                }
.......
```

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1065841/796c16ca-01da-0993-bd40-d48491e66dd1.gif" width=40%>　

<br>
・これで9割ができましたが、タップするスピードが早すぎですと、CardとCardが重なってしまいます。
解決するロジックもシンプルですが、Card①のアニメーションが終わるまで他のCardをdisableにします。

```swift:CoursesView.swift

@State var isDisabled = false

ForEach(courses) { item in
    CourseItem(course: item)
        .matchedGeometryEffect(
               id: item.id, in: namespace, isSource: !show
        )
           .frame(width: 335, height: 250)
           .onTapGesture {
                  withAnimation(.spring()) {
                         show.toggle()
                         selectedItem = item
                         isDisabled = true
                   }
            }
            .disabled(isDisabled)
}

```

・同様に、Cardの中でdisableにしたため、Listでdisableを外します。
 `DispatchQueue`を使ってアニメーションを自由に遅らせることができるので、0.5秒を遅らせば綺麗にできました。

```swift:CoursesView.swift

if selectedItem != nil {
     ScrollView {
          CourseItem(course: selectedItem!)
               .matchedGeometryEffect(
                    id: selectedItem!.id, in: namespace
                )
               .frame(height: 300)
               .onTapGesture {
                    withAnimation(.spring()) {
                          show.toggle()
                          selectedItem = nil
                          DispatchQueue.main.asyncAfter(
                               deadline: .now() + 0.5
                           ) {
                               isDisabled = false
                            }
                     }
                }
.......
```

<a id="#まとめ"></a>
# まとめ

**ソースコード[Github](https://github.com/wsimple1028/swiftUI_ios14)**

<a id="#reference"></a>
# 参考文献
- [Design Code --- SwiftUI for iOS 14](https://designcode.io/swiftui-ios14)