【SwiftUI】試して学ぶAnimationとTransition

# はじめに
先日、SwiftUIのAnimationとTransitionについて学習しました。

AnimationとTransitionの組み合わせによって挙動が変わったり、
Transition自身もcombineして組み合わせることができるといったように
柔軟な設定が可能なのですがその分わかりにくいです。

今回はAnimationとTransitionの組み合わせを実機で試すことができるアプリを作りました。

# 作ったもの
TransitionとcombineするTransition、及び付与するAnimationを
インサート時とリムーバル時それぞれ設定して挙動を確認することが可能です。

![001.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233342/25e26372-7a73-7475-5fb6-25501bfdc7c2.gif)

# コード
下記を丸っとコピーして実行します。

かなり殴り書きなコードですが、springのdurationなども指定できるように今後改良していきたいと思います。

```swift:Contentview.swift
import SwiftUI

struct ContentView: View {
    @State private var viewFlg: Bool = false
    @State private var isResume: Bool = false
    private let transitionList: [AnyTransition] = [.slide, .opacity, .scale, .move(edge: .top), .move(edge: .leading), .move(edge: .bottom), .move(edge: .trailing)]
    private let transitionStringList: [String] = ["slide", "opacity",
                                                  "scale", "move(edge: .top)", "move(edge: .leading)", "move(edge: .bottom)", "move(edge: .trailing)"]
    private let animationList: [Animation] = [.default, .linear, .easeIn, .easeOut, .easeInOut, .spring(response: 0.5, dampingFraction: 0.5, blendDuration: 1), .interactiveSpring(response: 0.5, dampingFraction: 0.5, blendDuration: 0.5)]
    private let animationStringList: [String] = [".default", ".linear", ".easeIn", ".easeOut", ".easeInOut", ".spring()", ".interactiveSpring()"]
    
    @State private var insertAnimationSelection: Int = 0
    @State private var removalAnimationSelection: Int = 0
    
    @State private var insertSelection: Int = 0
    @State private var removalSelection: Int = 0
    
    @State private var insertCmbSelection: Int = 0
    @State private var removalCmbSelection: Int = 0
    @State private var insertCombineFlg: Int = 0
    @State private var removalCombineFlg: Int = 0
    
    var body: some View {
        VStack {
            ZStack {
                Color.white
                // こいつにtransitionを反映する
                if viewFlg || !isResume {
                    Rectangle()
                        .fill(Color.blue)
                        .frame(width: 100, height: 40)
                        .transition(.asymmetric(
                                        insertion: insertCombineFlg == 0 ? transitionList[self.insertSelection] : transitionList[self.insertSelection].combined(with: transitionList[self.insertCmbSelection])
                                        ,removal: removalCombineFlg == 0 ? transitionList[self.removalSelection] : transitionList[self.removalSelection].combined(with: transitionList[self.removalCmbSelection])))
                        .zIndex(2)
                }
                
                VStack {
                    Spacer()
                    HStack {
                        Spacer()
                        if !isResume {
                            Button(action: {
                                self.isResume.toggle()
                                Timer.scheduledTimer(withTimeInterval: 0.5, repeats: false) {timer in
                                    withAnimation(animationList[self.insertAnimationSelection]) {
                                        self.viewFlg.toggle()
                                        Timer.scheduledTimer(withTimeInterval: 2.0, repeats: false) {timer in
                                            withAnimation(animationList[self.removalAnimationSelection]) {
                                                self.viewFlg.toggle()
                                            }
                                            Timer.scheduledTimer(withTimeInterval: 2.0, repeats: false) {timer in
                                                self.isResume.toggle()
                                            }
                                        }
                                    }
                                }
                            }, label: {
                                Image(systemName: "play.circle")
                                    .resizable()
                                    .frame(height: 40)
                                    .frame(width: 40)
                            })
                        }
                    }
                }
                .padding()
                .zIndex(1)
            }
            .frame(height: 200)
            
            
            
            NavigationView {
                VStack {
                    
                    Form {
                        Picker("", selection: self.$insertCombineFlg) {
                            Text("single").tag(0)
                            Text("combine").tag(1)
                        }
                        .pickerStyle(SegmentedPickerStyle())
                        
                        Picker(selection: $insertSelection, label: Text("InsertTransition"), content: {
                            ForEach(0 ..< transitionStringList.count) { num in
                                Text(self.transitionStringList[num])
                            }
                        })
                        
                        if self.insertCombineFlg == 1 {
                            Picker(selection: $insertCmbSelection, label: Text("withTransition"), content: {
                                ForEach(0 ..< transitionStringList.count) { num in
                                    Text(self.transitionStringList[num])
                                }
                            })
                        }
                        
                        Picker(selection: $insertAnimationSelection, label: Text("Animation"), content: {
                            ForEach(0 ..< animationStringList.count) { num in
                                Text(self.animationStringList[num])
                            }
                        })
                    }
                    
                    Form {
                        Picker("", selection: self.$removalCombineFlg) {
                            Text("single").tag(0)
                            Text("combine").tag(1)
                        }
                        .pickerStyle(SegmentedPickerStyle())
                        
                        Picker(selection: $removalSelection, label: Text("removalAnimation"), content: {
                            ForEach(0 ..< transitionStringList.count) { num in
                                Text(self.transitionStringList[num])
                            }
                        })
                        
                        if self.removalCombineFlg == 1 {
                            Picker(selection: $removalCmbSelection, label: Text("withTransition"), content: {
                                ForEach(0 ..< transitionStringList.count) { num in
                                    Text(self.transitionStringList[num])
                                }
                            })
                        }
                        
                        Picker(selection: $removalAnimationSelection, label: Text("Animation"), content: {
                            ForEach(0 ..< animationStringList.count) { num in
                                Text(self.animationStringList[num])
                            }
                        })
                    }
                    
                }            .navigationBarHidden(true)
            }
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

```