// カプセルアイテム
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()
}
}
}