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.
- π° 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
- iOS 17.0+
- Xcode 15.0+
- Swift 5.9+
- SwiftUI
RSCarousel is available through CocoaPods. To install it, simply add the following line to your Podfile:
pod 'RSCarousel'Then run:
pod installimport 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)
}
}
}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)
}
}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)
}
}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
)
}
)
}
}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
Display products, services, or features in an attractive carousel format:
- E-commerce product galleries
- Feature highlight sections
- Service offerings
- Portfolio items
Allow users to select from a variety of rewards or options:
- Gift selection screens
- Achievement rewards
- Subscription tiers
- Service packages
Create custom selection interfaces for:
- Food ordering apps (menu items)
- Event selection (time slots, dates)
- Customizable options (colors, sizes, styles)
- Category selection
Make onboarding more engaging:
- Feature introduction
- Tutorial steps
- Welcome screens
- Interactive guides
A SwiftUI view that displays a horizontally scrolling carousel of items with auto-spinning capabilities.
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 carouselspinInterval: Double?- Time interval (in seconds) between automatic scrolls. Passnilto disable auto-spin. Default is0.8onSelection: ((Data.Element) -> Void)?- Optional callback that's triggered when an item is selected (viastopAndSelectWinner())cardContent: (Data.Element) -> Content- A ViewBuilder closure that defines how each card should be rendered
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
onSelectioncallback with the selected item
carouselRef.stopAndSelectWinner()// 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)
}RSCarousel(
items,
onSelection: { selectedItem in
print("Selected: \(selectedItem)")
// Handle selection logic here
showCelebration()
updateUserRewards(selectedItem)
}
) { item in
CardView(item: item)
}- Ensure
spinIntervalis notnil - Check that your data collection is not empty
- Verify that items conform to
Identifiable
- Make sure you're running on iOS 17.0+
- Check that
visualEffectmodifier is available (iOS 17+ feature)
- The carousel uses lazy loading (
LazyHStack) for optimal performance - Consider limiting the number of items if you experience lag
Contributions are welcome! Please feel free to submit a Pull Request.
RSCarousel is available under the MIT license. See the LICENSE file for more info.
rajssinde
- Email: rajssinde@gmail.com
- GitHub: @rajssinde
- Built with β€οΈ using SwiftUI
- Inspired by modern carousel and slot machine interfaces
Made with β€οΈ for the iOS community