Here’s a SwiftUI implementation of a card carousel using HStack, ScrollView, and GeometryReader to arrange cards horizontally:
Explanation:
- Card Struct: A simple data model to hold information for each card (image, title, description).
- CardCarouselView:
cardsarray stores the card data.ScrollView(.horizontal)creates a horizontal scrolling view.HStackarranges the card views horizontally with spacing.ForEachiterates over thecardsarray to createCardViewfor each card.GeometryReaderis used to get the position of each card in the scroll view, allowing for the 3D rotation effect.
- CardView:
- Represents a single card in the carousel.
VStackarranges the image, title, and description vertically.Imagedisplays the card image (replace with actual image names).Textviews display the title and description.
- 3D Rotation Effect:
rotation3DEffectis used to apply a subtle 3D rotation to each card as it moves through the carousel. The angle of rotation is calculated based on the card’s position.
- Styling:
- Padding is added around the carousel and the content of each card.
- A white background, rounded corners, and a shadow are applied to each card for visual appeal.
import SwiftUI
import SwiftUI
struct Card: Identifiable {
let id = UUID()
let imageName: String
let title: String
let description: String
}
struct CardCarouselView: View {
let cards: [Card] = [
Card(imageName: "card1", title: "Card1", description: "Description for card 1"),
Card(imageName: "card2", title: "Card2", description: "Description for card 2"),
Card(imageName: "card3", title: "Card3", description: "Description for card 3"),
Card(imageName: "card4", title: "Card4", description: "Description for card 4"),
]
@State private var selectedIndex = 0 // Track the currently selected card
var body: some View {
ZStack {
Color(hex: 0xF5F5F5) // Light gray background color
.edgesIgnoringSafeArea(.all)
VStack {
Text("Featured Products") // Add title
.font(.title)
.fontWeight(.bold)
.padding(.top, 20)
Text("Explore our latest and greatest products.") // Add description
.font(.subheadline)
.foregroundColor(.secondary)
.padding(.bottom, 10)
ScrollView(.horizontal, showsIndicators: false) {
HStack(spacing: 20) {
ForEach(cards.indices, id: \.self) { index in
GeometryReader { geometry in
CardView(card: cards[index])
.rotation3DEffect(Angle(degrees: Double(geometry.frame(in: .global).minX - 30) / -20), axis: (x: 0, y: 1, z: 0))
.onTapGesture {
withAnimation {
selectedIndex = index
}
}
}
.frame(width: 300, height: 250)
}
}
.padding(.horizontal, 30)
// .padding(.bottom, 30)
}
.background(Color(hex: 0xF5F5F5)) // Light gray background (F5F5F5)
HStack { // Page indicator dots
ForEach(0..<cards.count, id: \.self) { index in
Circle()
.fill(index == selectedIndex ? Color.blue : Color.gray)
.frame(width: 8, height: 8)
}
}
.padding(.bottom, 20)
}
}
}
}
struct CardView: View {
let card: Card
var body: some View {
VStack(alignment: .leading) {
Image(card.imageName)
.resizable()
.scaledToFill()
.frame(height: 100)
.clipped()
Text(card.title)
.font(.headline)
.padding(.top, 5)
Text(card.description)
.font(.caption)
}
.padding()
.background(Color.white)
.cornerRadius(10)
.shadow(radius: 3)
}
}
#Preview {
CardCarouselView()
}
extension Color {
init(hex: UInt, alpha: Double = 1) {
self.init(
.sRGB,
red: Double((hex >> 16) & 0xff) / 255,
green: Double((hex >> 8) & 0xff) / 255,
blue: Double(hex & 0xff) / 255,
opacity: alpha
)
}
}
import SwiftUI
import SwiftUI
struct Card: Identifiable {
let id = UUID()
let imageName: String
let title: String
let description: String
}
struct CardCarouselView: View {
let cards: [Card] = [
Card(imageName: "card1", title: "Card1", description: "Description for card 1"),
Card(imageName: "card2", title: "Card2", description: "Description for card 2"),
Card(imageName: "card3", title: "Card3", description: "Description for card 3"),
Card(imageName: "card4", title: "Card4", description: "Description for card 4"),
]
@State private var selectedIndex = 0 // Track the currently selected card
var body: some View {
ZStack {
Color(hex: 0xF5F5F5) // Light gray background color
.edgesIgnoringSafeArea(.all)
VStack {
Text("Featured Products") // Add title
.font(.title)
.fontWeight(.bold)
.padding(.top, 20)
Text("Explore our latest and greatest products.") // Add description
.font(.subheadline)
.foregroundColor(.secondary)
.padding(.bottom, 10)
ScrollView(.horizontal, showsIndicators: false) {
HStack(spacing: 20) {
ForEach(cards.indices, id: \.self) { index in
GeometryReader { geometry in
CardView(card: cards[index])
.rotation3DEffect(Angle(degrees: Double(geometry.frame(in: .global).minX - 30) / -20), axis: (x: 0, y: 1, z: 0))
.onTapGesture {
withAnimation {
selectedIndex = index
}
}
}
.frame(width: 300, height: 250)
}
}
.padding(.horizontal, 30)
// .padding(.bottom, 30)
}
.background(Color(hex: 0xF5F5F5)) // Light gray background (F5F5F5)
HStack { // Page indicator dots
ForEach(0..<cards.count, id: \.self) { index in
Circle()
.fill(index == selectedIndex ? Color.blue : Color.gray)
.frame(width: 8, height: 8)
}
}
.padding(.bottom, 20)
}
}
}
}
struct CardView: View {
let card: Card
var body: some View {
VStack(alignment: .leading) {
Image(card.imageName)
.resizable()
.scaledToFill()
.frame(height: 100)
.clipped()
Text(card.title)
.font(.headline)
.padding(.top, 5)
Text(card.description)
.font(.caption)
}
.padding()
.background(Color.white)
.cornerRadius(10)
.shadow(radius: 3)
}
}
#Preview {
CardCarouselView()
}
extension Color {
init(hex: UInt, alpha: Double = 1) {
self.init(
.sRGB,
red: Double((hex >> 16) & 0xff) / 255,
green: Double((hex >> 8) & 0xff) / 255,
blue: Double(hex & 0xff) / 255,
opacity: alpha
)
}
}
Key Improvements:
- Horizontal Scrolling: The
ScrollViewwith.horizontalallows users to swipe horizontally to browse through the cards. - Card Layout:
HStackarranges the cards side-by-side. - Dynamic Cards:
ForEachdynamically creates card views based on thecardsdata. - Visual Appeal: Each card has a visually appealing layout with an image, title, description, rounded corners, and a shadow.
- 3D Rotation Effect: Adds a touch of interactivity and depth to the carousel.
How to Use:
- Replace the sample
cardsdata with your actual card content (images, titles, descriptions). - Customize the appearance of the cards and the carousel to match your design preferences.