Created
April 20, 2025 18:13
-
-
Save Codelaby/c99b9d6d3942792226405f17e9baec32 to your computer and use it in GitHub Desktop.
Play/pause loop animation swiftui
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import SwiftUI | |
struct Cancel: CustomAnimation { | |
func animate<V: VectorArithmetic>( | |
value: V, time: TimeInterval, context: inout AnimationContext<V> | |
) -> V? { | |
return nil // Fin inmediato | |
} | |
func shouldMerge<V>( | |
previous: Animation, value: V, time: TimeInterval, context: inout AnimationContext<V> | |
) -> Bool where V : VectorArithmetic { | |
return true | |
} | |
} | |
struct AnimatableNumberModifier: Animatable, ViewModifier { | |
var animatableData: Double | |
@Binding var currentAngle: Double | |
init(number: Double, currentAngle: Binding<Double>) { | |
self.animatableData = number | |
self._currentAngle = currentAngle | |
} | |
func body(content: Content) -> some View { | |
content | |
.rotationEffect(.degrees(animatableData)) | |
.onChange(of: animatableData) { _, newValue in | |
currentAngle = newValue.truncatingRemainder(dividingBy: 360) | |
} | |
} | |
} | |
extension View { | |
func numericAnimation(for number: Double, currentAngle: Binding<Double>) -> some View { | |
modifier(AnimatableNumberModifier(number: number, currentAngle: currentAngle)) | |
} | |
} | |
struct TapableVinylView2: View { | |
enum SpinningStatus { | |
case spinning | |
case paused | |
case stopped | |
} | |
@State private var spinningStatus: SpinningStatus = .stopped | |
@State private var angle: Double = 0.0 | |
@State private var currentAngle: Double = 0.0 | |
var body: some View { | |
VStack { | |
Image("rose_apt_vinyl") | |
.resizable() | |
.frame(width: 256, height: 256) | |
.numericAnimation(for: angle, currentAngle: $currentAngle) | |
.onTapGesture { | |
handleTap() | |
} | |
.onLongPressGesture { | |
handleLongPress() | |
} | |
HStack { | |
Button("Play") { | |
restart() | |
} | |
.disabled(spinningStatus == .spinning) | |
Button("Pause") { | |
pause() | |
} | |
.disabled(spinningStatus != .spinning) | |
Button("Stop") { | |
stop() | |
} | |
.disabled(spinningStatus != .paused) | |
} | |
.padding() | |
} | |
} | |
private func handleTap() { | |
switch spinningStatus { | |
case .spinning: | |
pause() | |
case .paused: | |
restart() | |
case .stopped: | |
restart() | |
} | |
} | |
private func handleLongPress() { | |
stop() | |
} | |
private func restart() { | |
guard spinningStatus != .spinning else { return } | |
spinningStatus = .spinning | |
updateSpinning() | |
} | |
private func pause() { | |
spinningStatus = .paused | |
updateSpinning() | |
angle = currentAngle | |
} | |
private func stop() { | |
spinningStatus = .stopped | |
updateSpinning() | |
angle = currentAngle | |
withAnimation(.smooth) { | |
angle = angle - currentAngle | |
} | |
} | |
private func updateSpinning() { | |
withAnimation(spinningStatus == .spinning | |
? .linear(duration: 3).repeatForever(autoreverses: false) | |
: .init(Cancel())) { | |
angle += 360 | |
} | |
} | |
} | |
#Preview { | |
TapableVinylView2() | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment