SwiftUIでToastっぽいバナーっぽいやつを実装してみる

## こんな感じにしたい

### [PowerplayToastKit: A Simple Toast Library for iOS](https://iosexample.com/powerplaytoastkit-a-simple-toast-library-for-ios/)
とか
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245300/32d23c51-e880-e4d8-5299-036570fc2975.png)

### 翻訳のポップアップ
とか
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245300/bfeb911e-7287-0e93-47fb-c3a4cbc17f45.png)


## 実装-Success
こんな感じで3秒経つと消える感じです
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245300/2c566569-5c9f-348b-2b76-2b110c8b5a75.png)

```ToastBanner.swift
import SwiftUI

struct ToastBanner: View {
    
    @Binding var showFlag: Bool
    
    var body: some View {
        ZStack {
            VStack {
                HStack {
                    Image(systemName: "checkmark.circle.fill")
                        .resizable()
                        .scaledToFit()
                        .frame(height: 30)
                        .foregroundColor(Color.green)
                    VStack(alignment: .leading) {
                        Text("ダウンロード完了")
                            .font(.custom("RoundedMplus1c-Bold", size: 16))
                            .foregroundColor(Color.black)
                        Text("ファイルを写真アプリに保存しました")
                            .font(.custom("RoundedMplus1c-Regular", size: 14))
                            .foregroundColor(Color.black)
                    }
                }
                .padding(.all, 10)
                // PaleGreenっぽいやつ #D0E2BE
                .background(Color(red: 232/255, green: 242/255, blue: 228/255))
                .clipShape(RoundedRectangle(cornerRadius: 10))
                Spacer()
            }
        }
        .padding(.top, 50)
        .onAppear {
            DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
                withAnimation {
                    showFlag = false
                }
            }
        }
    }
}

struct ToastBanner_Previews: PreviewProvider {
    static var flag = State(initialValue: true)
    
    static var previews: some View {
        ToastBanner(showFlag: flag.projectedValue)
    }
}
```

## 実装-Failure
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245300/5fd7f778-c3e6-dfba-db65-2d2bee2287ba.png)

ちょっと使いまわしてフラグで成否切り替えするようにしてみる。

```ToastBanner.swift
struct ToastBanner: View {
    /// バナー表示フラグ
    @Binding var showFlag: Bool
    /// ダウンロード成否表示切替フラグ
    @State var shouldShowDownloadSuccess: Bool
    
    var body: some View {
        Group {
            if shouldShowDownloadSuccess {
                SuccessBanner()
            } else {
                FailureBanner()
            }
        }
        .padding(.top, 50)
        .onAppear {
            DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
                withAnimation {
                    showFlag = false
                }
            }
        }
    }
}

private struct SuccessBanner: View {
    var body: some View {
        ZStack {
            VStack {
                HStack {
                    Image(systemName: "checkmark.circle.fill")
                        .resizable()
                        .scaledToFit()
                        .frame(height: 30)
                        .foregroundColor(Color.green)
                    VStack(alignment: .leading) {
                        Text("ダウンロード完了")
                            .font(.custom("RoundedMplus1c-Bold", size: 16))
                            .foregroundColor(Color.black)
                        Text("ファイルを写真アプリに保存しました")
                            .font(.custom("RoundedMplus1c-Regular", size: 14))
                            .foregroundColor(Color.black)
                    }
                }
                .padding(.all, 10)
                // PaleGreenっぽいやつ #D0E2BE
                .background(Color(red: 232/255, green: 242/255, blue: 228/255))
                .clipShape(RoundedRectangle(cornerRadius: 10))
                Spacer()
            }
        }
    }
}

private struct FailureBanner: View {
    var body: some View {
        ZStack {
            VStack {
                HStack {
                    Image(systemName: "xmark.circle.fill")
                        .resizable()
                        .scaledToFit()
                        .frame(height: 30)
                        .foregroundColor(Color.red)
                    VStack(alignment: .leading) {
                        Text("ダウンロード失敗")
                            .font(.custom("RoundedMplus1c-Bold", size: 16))
                            .foregroundColor(Color.black)
                        Text("ファイルの保存に失敗しました")
                            .font(.custom("RoundedMplus1c-Regular", size: 14))
                            .foregroundColor(Color.black)
                    }
                }
                .padding(.all, 10)
                // Light Red #F4E4DE
                .background(Color(red: 244/255, green: 228/255, blue: 222/255))
                .clipShape(RoundedRectangle(cornerRadius: 10))
                Spacer()
            }
        }
    }
}

struct ToastBanner_Previews: PreviewProvider {
    static var flag = State(initialValue: true)
    
    static var previews: some View {
        ToastBanner(showFlag: flag.projectedValue, shouldShowDownloadSuccess: false)
    }
}
```

## 参考
[PowerplayToastKit: A Simple Toast Library for iOS](https://iosexample.com/powerplaytoastkit-a-simple-toast-library-for-ios/)
[薄緑（うすみどり）の色見本・カラーコード](https://www.i-iro.com/dic/usumidori#:~:text=%E8%96%84%E7%B7%91%E3%81%AF%E8%8B%A5%E7%B7%91%E3%82%88%E3%82%8A,%E3%82%87%E3%81%8F%E3%80%8D%E3%81%A8%E8%AA%AD%E3%81%BF%E3%81%BE%E3%81%99%E3%80%82)
[カラーコード変換ツール](https://tech-unlimited.com/color.html)
[SwiftUI でトースト・バナーを表示する方法](https://zenn.dev/woo_noo/articles/17f05786009d563ab498)