Skip to content

Instantly share code, notes, and snippets.

@hassanvfx
Created January 2, 2024 06:19
Show Gist options
  • Save hassanvfx/a0616ae77001d6ea6d3c11d21d4a9c44 to your computer and use it in GitHub Desktop.
Save hassanvfx/a0616ae77001d6ea6d3c11d21d4a9c44 to your computer and use it in GitHub Desktop.
swiftui rouletter wheel
import SwiftUI
struct RouletteWheel: View {
let segments: [String]
var onResultSelected: (String) -> Void
@State private var rotationAngle: Double = 0
@State private var isSpinning: Bool = false
var body: some View {
ZStack {
ForEach(0..<segments.count, id: \.self) { index in
RouletteSegment(
label: segments[index],
segmentCount: segments.count,
index: index
)
}
.frame(width: 320, height: 320, alignment: .center)
.rotationEffect(.degrees(rotationAngle))
TriangleIndicator()
.fill(Color.red)
.frame(width: 30, height: 30)
.offset(y: -160)
.shadow(radius: 4)
}
.onTapGesture {
if !isSpinning {
let fullRotation = 360.0
let rotations = Double.random(in: 5...10)
let totalDegrees = fullRotation * rotations
let animation = Animation.easeOut(duration: 3)
withAnimation(animation) {
rotationAngle += totalDegrees
}
// Determine the winning segment after the animation
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
isSpinning = false
// Normalize the angle between 0 to 359
let normalizedRotation = rotationAngle.truncatingRemainder(dividingBy: fullRotation)
// Compute the winning segment
let segmentAngle = fullRotation / Double(segments.count)
let winningIndex = Int((fullRotation - normalizedRotation) / segmentAngle) % segments.count
let winningSegment = segments[winningIndex]
onResultSelected(winningSegment)
}
isSpinning = true
}
}
}
}
struct RouletteSegment: View {
var label: String
var segmentCount: Int
var index: Int
var body: some View {
GeometryReader { geometry in
Path { path in
let center = CGPoint(x: geometry.size.width / 2, y: geometry.size.height / 2)
path.move(to: center)
path.addArc(center: center, radius: geometry.size.width / 2,
startAngle: .degrees((360 / Double(segmentCount)) * Double(index)),
endAngle: .degrees((360 / Double(segmentCount)) * Double(index + 1)),
clockwise: false)
}
.fill(segmentColor(index: index))
Text(label)
.position(segmentLabelPosition(geometry: geometry, index: index, segmentCount: segmentCount))
}
}
private func segmentColor(index: Int) -> Color {
return [Color.red, Color.green, Color.blue, Color.orange, Color.purple, Color.yellow][index % 6]
}
private func segmentLabelPosition(geometry: GeometryProxy, index: Int, segmentCount: Int) -> CGPoint {
let angle = (2 * .pi / Double(segmentCount)) * Double(index) + (.pi / Double(segmentCount))
let radius = geometry.size.width / 3
let labelX = geometry.size.width / 2 + cos(angle) * radius
let labelY = geometry.size.height / 2 + sin(angle) * radius
return CGPoint(x: labelX, y: labelY)
}
}
struct TriangleIndicator: Shape {
func path(in rect: CGRect) -> Path {
var path = Path()
let center = CGPoint(x: rect.midX, y: rect.minY)
let left = CGPoint(x: rect.minX, y: rect.maxY)
let right = CGPoint(x: rect.maxX, y: rect.maxY)
path.move(to: center)
path.addLines([left, right, center])
return path
}
}
struct RouletteApp_Preview: PreviewProvider {
static var previews: some View {
RouletteWheel(segments: ["Option 1", "Option 2", "Option 3", "Option 4", "Option 5"]){_ in}
}
}
@hassanvfx
Copy link
Author

image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment