[Xcode/SwiftUI] カプセルタイプのカテゴリー選択ビューを作ってみる 💊

実装

// カプセルアイテム
struct CapsuleButton: View {
    let label: String
    let isSelected: Bool
    let onTap: () -> Void

    var body: some View {
        Button(action: onTap) {
            Text(label)
                .font(.subheadline.bold())
                .padding(.horizontal, 18)
                .padding(.vertical, 10)
                .background(
                    ZStack {
                        if isSelected {
                            LinearGradient(
                                gradient: Gradient(colors: [Color.blue, Color.purple]),
                                startPoint: .leading,
                                endPoint: .trailing
                            )
                        } else {
                            Color(.systemGray6)
                        }
                    }
                )
                .foregroundColor(isSelected ? .white : .primary)
                .clipShape(Capsule())
                .shadow(color: isSelected ? Color.black.opacity(0.2) : Color.clear, radius: 4, x: 0, y: 2)
                .animation(.easeInOut(duration: 0.25), value: isSelected)
        }
        .buttonStyle(.plain)
    }
}

// メインView
struct HomeView: View {
    // MARK: - Properties
    @State private var selectedCategories: [String] = []
    // ダミーデータ 
    private let categories = [
        "🔥 Trending",
        "🎵 Music",
        "📚 Books",
        "🎮 Games",
        "🎬 Movies",
        "📸 Photos",
        "🧘‍♂️ Health",
        "🌍 Travel",
        "🍔 Food",
        "🎨 Art",
        "🏀 Sports",
        "👗 Fashion",
        "🎉 Events",
        "💻 Tech",
        "🏡 Home",
        "🐶 Pets",
        "💡 Ideas",
        "🚗 Cars",
        "✈️ Flights",
        "🧩 Hobbies",
        "🛠 DIY",
        "🍃 Nature",
        "👶 Kids",
        "📈 Business",
        "📷 Vlogging",
        "🎙 Podcasts",
        "📺 TV Shows",
        "👟 Fitness",
        "🧠 Education",
        "🌌 Space"
    ]

    private let gridLayout = [
        GridItem(.adaptive(minimum: 120), spacing: 12)
    ]

    // MARK: - Body
    var body: some View {
        ScrollView(.vertical, showsIndicators: false) {
            LazyVGrid(columns: gridLayout, spacing: 16) {
                ForEach(categories, id: \.self) { category in
                    CapsuleButton(
                        label: category,
                        isSelected: selectedCategories.contains(category)
                    ) {
                        if selectedCategories.contains(category) {
                            // 選択解除
                            selectedCategories.removeAll { $0 == category }
                        } else {
                            // 選択
                            selectedCategories.append(category)
                        }
                    }
                }
            }
            .padding()
        }
    }
}