🪶

SwiftUIで、FlashCardを作ってみた

2024/08/17に公開

Swaipeすると横にスライドするUI

SwiftUIで、スワイプすると、横にスライドするUIを作りたかった。YouTubeで検索すると、動画色々出てくるが自分の作りたいものと違った😅

こんな感じのものを作ってみた!
https://youtube.com/shorts/nSHgusZwFsU

アニメーションの機能も必要なようだ...
流石にわからなかったので、AIに試しにサンプルを作らせてみた。

イメージと違うな。カードを横にスワイプスるとカードが斜めに動いて、切り替わるようにしてほしい。アニメーションが必要なのか?

ご指摘ありがとうございます。確かに、よりダイナミックなアニメーションを加えることで、ユーザー体験を向上させることができますね。カードを横にスワイプすると斜めに動いて切り替わるアニメーションを実装しましょう。
はい、このような動きを実現するにはアニメーションが必要です。SwiftUIのwithAnimationとgestureを組み合わせて、より洗練された動きを作り出すことができます。
では、先ほどのコードを修正して、要望に沿ったアニメーションを追加しましょう。

import SwiftUI

struct Flashcard: Identifiable {
    let id = UUID()
    let title: String
    let content: String
}

struct ContentView: View {
    @State private var flashcards = [
        Flashcard(title: "コーヒーショップの場所", content: "Where is the coffee shop after exiting the station?"),
        Flashcard(title: "レストランの予約", content: "I'd like to make a reservation for dinner."),
        Flashcard(title: "電車の乗り方", content: "How do I take the train to the city center?"),
        Flashcard(title: "観光スポットの推薦", content: "Can you recommend some popular tourist attractions?"),
        Flashcard(title: "ホテルのチェックイン時間", content: "What time is check-in at the hotel?")
    ]
    
    @State private var currentIndex = 0
    @State private var offset: CGSize = .zero
    @State private var backgroundColor = Color.random
    
    var body: some View {
        VStack {
            Spacer()
            ZStack {
                ForEach(0..<flashcards.count) { index in
                    if index == currentIndex {
                        flashcardView(for: index)
                            .offset(offset)
                            .rotationEffect(.degrees(Double(offset.width / 5)))
                            .gesture(
                                DragGesture()
                                    .onChanged { gesture in
                                        offset = gesture.translation
                                    }
                                    .onEnded { gesture in
                                        withAnimation(.spring()) {
                                            if abs(gesture.translation.width) > 100 {
                                                offset = CGSize(width: gesture.translation.width > 0 ? 1000 : -1000, height: 0)
                                                DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
                                                    changeCard(gesture.translation.width > 0 ? -1 : 1)
                                                }
                                            } else {
                                                offset = .zero
                                            }
                                        }
                                    }
                            )
                    }
                }
            }
            Spacer()
        }
    }
    
    func flashcardView(for index: Int) -> some View {
        VStack {
            Text(flashcards[index].title)
                .font(.headline)
                .padding()
            
            Text(flashcards[index].content)
                .font(.body)
                .padding()
        }
        .frame(width: 300, height: 200)
        .background(backgroundColor)
        .cornerRadius(10)
        .shadow(radius: 5)
    }
    
    func changeCard(_ direction: Int) {
        currentIndex = (currentIndex + direction + flashcards.count) % flashcards.count
        offset = .zero
        backgroundColor = Color.random
    }
}

extension Color {
    static var random: Color {
        return Color(
            red: .random(in: 0...1),
            green: .random(in: 0...1),
            blue: .random(in: 0...1)
        )
    }
}

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

このコードでの主な変更点と新機能は以下の通りです:

  1. カードのスワイプアニメーション:

offsetとrotationEffectを使用して、カードがスワイプ中に移動し回転するようにしました。
スワイプが一定距離を超えると、カードが画面外に飛び出すアニメーションを追加しました。

  1. カード切り替えのロジック:

スワイプが終了したときに、スワイプの距離に基づいてカードを切り替えるか元の位置に戻すかを決定します。
カードが切り替わる際に、新しいカードが表示されるまでの短い遅延を追加しました。

  1. 背景色の変更:

カードが切り替わるたびに新しいランダムな背景色を設定します。

  1. ZStackの使用:

現在のカードのみを表示するようにZStackを使用しています。これにより、スワイプ中に次のカードが見えることを防いでいます。

このアプリケーションでは、カードを横にスワイプすると、カードが斜めに動いて切り替わります。スワイプの距離が十分でない場合は、カードが元の位置に戻ります。
また、カードが切り替わるたびに背景色がランダムに変更されるので、視覚的な変化も楽しめます。
このコードをSwiftUIプロジェクトに組み込んで実行すると、要望に沿ったダイナミックなフラッシュカードアプリケーションが動作するはずです。何か追加の機能や修正点があればお知らせください。

感想

もっと美しいUIを作っている人はいるが、動きがあるカードがあれば、十分だったので、今回はこんな感じにした。くるくる回りながら、スライドとかもありそう?
みていても疲れないものにしたいですね。

アニメーションがあるリッチなアプリケーションができる。

Discussion