Here’s a SwiftUI implementation of a card carousel using HStack, ScrollView, and GeometryReader to arrange cards horizontally:

Explanation:

  1. Card Struct: A simple data model to hold information for each card (image, title, description).
  2. CardCarouselView:
    • cards array stores the card data.
    • ScrollView(.horizontal) creates a horizontal scrolling view.
    • HStack arranges the card views horizontally with spacing.
    • ForEach iterates over the cards array to create CardView for each card.
    • GeometryReader is used to get the position of each card in the scroll view, allowing for the 3D rotation effect.
  3. CardView:
    • Represents a single card in the carousel.
    • VStack arranges the image, title, and description vertically.
    • Image displays the card image (replace with actual image names).
    • Text views display the title and description.
  4. 3D Rotation Effect:
    • rotation3DEffect is 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.
  5. 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 ScrollView with .horizontal allows users to swipe horizontally to browse through the cards.
  • Card Layout: HStack arranges the cards side-by-side.
  • Dynamic Cards: ForEach dynamically creates card views based on the cards data.
  • 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 cards data with your actual card content (images, titles, descriptions).
  • Customize the appearance of the cards and the carousel to match your design preferences.