Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

3 Commits
Β 
Β 
Β 
Β 

Repository files navigation

RSCarousel

CI Status Version License Platform

RSCarousel is a beautiful, customizable SwiftUI carousel component with auto-spinning capabilities, perfect for creating engaging "Spin to Win" experiences, prize wheels, and interactive card carousels in your iOS applications.

✨ Features

  • 🎰 Auto-Spinning Carousel - Automatically scrolls through items at configurable intervals
  • 🎨 Stunning 3D Effects - Beautiful rotation, scale, and opacity transitions
  • 🎯 Spin to Win - Built-in functionality to randomly select a winner with smooth animations
  • πŸ“± SwiftUI Native - Built entirely with SwiftUI for modern iOS development
  • ⚑ High Performance - Optimized with lazy loading and efficient rendering
  • πŸŽ›οΈ Fully Customizable - Complete control over card content and behavior
  • πŸ“³ Haptic Feedback - Integrated haptic feedback for better user experience
  • πŸ”„ Infinite Scrolling - Seamless infinite loop scrolling experience

πŸ“‹ Requirements

  • iOS 17.0+
  • Xcode 15.0+
  • Swift 5.9+
  • SwiftUI

πŸ“¦ Installation

RSCarousel is available through CocoaPods. To install it, simply add the following line to your Podfile:

pod 'RSCarousel'

Then run:

pod install

πŸš€ Usage

Basic Example

import SwiftUI
import RSCarousel

struct ContentView: View {
    let prizes = [
        Prize(id: 1, name: "Prize 1", color: .red),
        Prize(id: 2, name: "Prize 2", color: .blue),
        Prize(id: 3, name: "Prize 3", color: .green),
        Prize(id: 4, name: "Prize 4", color: .yellow),
        Prize(id: 5, name: "Prize 5", color: .purple)
    ]
    
    var body: some View {
        RSCarousel(prizes, spinInterval: 0.8) { prize in
            PrizeCard(prize: prize)
        }
        .frame(height: 300)
    }
}

struct Prize: Identifiable {
    let id: Int
    let name: String
    let color: Color
}

struct PrizeCard: View {
    let prize: Prize
    
    var body: some View {
        RoundedRectangle(cornerRadius: 20)
            .fill(prize.color.gradient)
            .overlay {
                Text(prize.name)
                    .font(.title)
                    .fontWeight(.bold)
                    .foregroundColor(.white)
            }
    }
}

Spin to Win Implementation

import SwiftUI
import RSCarousel

struct SpinToWinView: View {
    @State private var carouselRef: RSCarousel<Prize, PrizeCard>?
    @State private var winner: Prize?
    @State private var isSpinning = false
    
    let prizes: [Prize] = [
        Prize(id: 1, name: "iPhone", icon: "πŸ“±"),
        Prize(id: 2, name: "MacBook", icon: "πŸ’»"),
        Prize(id: 3, name: "iPad", icon: "πŸ“±"),
        Prize(id: 4, name: "AirPods", icon: "🎧"),
        Prize(id: 5, name: "Watch", icon: "⌚"),
        Prize(id: 6, name: "Camera", icon: "πŸ“·")
    ]
    
    var body: some View {
        VStack(spacing: 30) {
            // Carousel
            RSCarousel(
                prizes,
                spinInterval: isSpinning ? 0.6 : nil,
                onSelection: { selectedPrize in
                    winner = selectedPrize
                    isSpinning = false
                }
            ) { prize in
                PrizeCardView(prize: prize)
            }
            .frame(height: 250)
            
            // Winner Display
            if let winner = winner {
                VStack(spacing: 10) {
                    Text("πŸŽ‰ Congratulations! πŸŽ‰")
                        .font(.title2)
                        .fontWeight(.bold)
                    Text("You won: \(winner.name) \(winner.icon)")
                        .font(.title3)
                        .foregroundColor(.secondary)
                }
                .padding()
                .background(Color.green.opacity(0.1))
                .cornerRadius(15)
            }
            
            // Spin Button
            Button(action: {
                startSpin()
            }) {
                Text(isSpinning ? "Spinning..." : "Spin to Win!")
                    .font(.headline)
                    .foregroundColor(.white)
                    .frame(maxWidth: .infinity)
                    .padding()
                    .background(isSpinning ? Color.gray : Color.blue)
                    .cornerRadius(15)
            }
            .disabled(isSpinning)
            .padding(.horizontal)
        }
        .padding()
    }
    
    private func startSpin() {
        isSpinning = true
        // Auto-stop after a few seconds
        DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
            carouselRef?.stopAndSelectWinner()
        }
    }
}

struct PrizeCardView: View {
    let prize: Prize
    
    var body: some View {
        VStack(spacing: 20) {
            Text(prize.icon)
                .font(.system(size: 60))
            Text(prize.name)
                .font(.title2)
                .fontWeight(.semibold)
                .foregroundColor(.primary)
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity)
        .background(Color(.systemBackground))
        .cornerRadius(20)
        .shadow(color: .black.opacity(0.2), radius: 10, x: 0, y: 5)
    }
}

Disable Auto-Spin (Manual Control)

import SwiftUI
import RSCarousel

struct ManualCarouselView: View {
    let items = (1...10).map { Item(id: $0, title: "Item \($0)") }
    
    var body: some View {
        RSCarousel(items, spinInterval: nil) { item in
            ItemCard(item: item)
        }
        .frame(height: 200)
    }
}

struct Item: Identifiable {
    let id: Int
    let title: String
}

struct ItemCard: View {
    let item: Item
    
    var body: some View {
        Text(item.title)
            .font(.headline)
            .frame(maxWidth: .infinity, maxHeight: .infinity)
            .background(
                LinearGradient(
                    colors: [.blue, .purple],
                    startPoint: .topLeading,
                    endPoint: .bottomTrailing
                )
            )
            .foregroundColor(.white)
            .cornerRadius(15)
    }
}

Custom Card Styling

import SwiftUI
import RSCarousel

struct CustomStyledCarousel: View {
    let products = [
        Product(id: 1, name: "Product A", price: "$99", image: "star.fill"),
        Product(id: 2, name: "Product B", price: "$149", image: "heart.fill"),
        Product(id: 3, name: "Product C", price: "$199", image: "bolt.fill")
    ]
    
    var body: some View {
        RSCarousel(products, spinInterval: 1.0) { product in
            ProductCard(product: product)
        }
        .frame(height: 350)
    }
}

struct Product: Identifiable {
    let id: Int
    let name: String
    let price: String
    let image: String
}

struct ProductCard: View {
    let product: Product
    
    var body: some View {
        VStack(spacing: 20) {
            Image(systemName: product.image)
                .font(.system(size: 80))
                .foregroundStyle(
                    LinearGradient(
                        colors: [.pink, .orange],
                        startPoint: .top,
                        endPoint: .bottom
                    )
                )
            
            VStack(spacing: 8) {
                Text(product.name)
                    .font(.title2)
                    .fontWeight(.bold)
                
                Text(product.price)
                    .font(.title3)
                    .foregroundColor(.secondary)
            }
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity)
        .padding(30)
        .background(
            RoundedRectangle(cornerRadius: 25)
                .fill(Color(.systemGray6))
                .overlay {
                    RoundedRectangle(cornerRadius: 25)
                        .stroke(
                            LinearGradient(
                                colors: [.blue.opacity(0.3), .purple.opacity(0.3)],
                                startPoint: .topLeading,
                                endPoint: .bottomTrailing
                            ),
                            lineWidth: 2
                        )
                }
        )
    }
}

🎯 Use Cases

1. Spin to Win / Prize Wheel

Create engaging gamification experiences where users can spin to win prizes, rewards, or discounts. Perfect for:

  • Mobile gaming apps
  • E-commerce promotions
  • Marketing campaigns
  • Loyalty programs

2. Product Showcase Carousel

Display products, services, or features in an attractive carousel format:

  • E-commerce product galleries
  • Feature highlight sections
  • Service offerings
  • Portfolio items

3. Reward Selection Interface

Allow users to select from a variety of rewards or options:

  • Gift selection screens
  • Achievement rewards
  • Subscription tiers
  • Service packages

4. Interactive Selection Wheel

Create custom selection interfaces for:

  • Food ordering apps (menu items)
  • Event selection (time slots, dates)
  • Customizable options (colors, sizes, styles)
  • Category selection

5. Gamified Onboarding

Make onboarding more engaging:

  • Feature introduction
  • Tutorial steps
  • Welcome screens
  • Interactive guides

πŸ“š API Documentation

RSCarousel<Data, Content>

A SwiftUI view that displays a horizontally scrolling carousel of items with auto-spinning capabilities.

Initializer

public init(
    _ data: Data,
    spinInterval: Double? = 0.8,
    onSelection: ((Data.Element) -> Void)? = nil,
    @ViewBuilder cardContent: @escaping (Data.Element) -> Content
)

Parameters:

  • data: Data - A collection of identifiable items to display in the carousel
  • spinInterval: Double? - Time interval (in seconds) between automatic scrolls. Pass nil to disable auto-spin. Default is 0.8
  • onSelection: ((Data.Element) -> Void)? - Optional callback that's triggered when an item is selected (via stopAndSelectWinner())
  • cardContent: (Data.Element) -> Content - A ViewBuilder closure that defines how each card should be rendered

Methods

stopAndSelectWinner()

Manually triggers the "Spin to Win" logic. This method:

  • Stops the auto-scrolling
  • Randomly selects an item from the data collection
  • Animates the carousel to the selected item with a spring animation
  • Triggers the onSelection callback with the selected item
carouselRef.stopAndSelectWinner()

🎨 Customization Tips

Adjusting Spin Speed

// Faster spinning
RSCarousel(items, spinInterval: 0.5) { item in
    CardView(item: item)
}

// Slower spinning
RSCarousel(items, spinInterval: 1.2) { item in
    CardView(item: item)
}

// Manual control only
RSCarousel(items, spinInterval: nil) { item in
    CardView(item: item)
}

Handling Selection Callbacks

RSCarousel(
    items,
    onSelection: { selectedItem in
        print("Selected: \(selectedItem)")
        // Handle selection logic here
        showCelebration()
        updateUserRewards(selectedItem)
    }
) { item in
    CardView(item: item)
}

πŸ› Troubleshooting

Carousel not spinning

  • Ensure spinInterval is not nil
  • Check that your data collection is not empty
  • Verify that items conform to Identifiable

Animation issues

  • Make sure you're running on iOS 17.0+
  • Check that visualEffect modifier is available (iOS 17+ feature)

Performance concerns

  • The carousel uses lazy loading (LazyHStack) for optimal performance
  • Consider limiting the number of items if you experience lag

🀝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

πŸ“ License

RSCarousel is available under the MIT license. See the LICENSE file for more info.

πŸ‘€ Author

rajssinde

πŸ™ Acknowledgments

  • Built with ❀️ using SwiftUI
  • Inspired by modern carousel and slot machine interfaces

Made with ❀️ for the iOS community

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors