swift - Proper way to stop an infinitely rotating image? and how does one implement removeAllAnimations? -
i'd use button toggle – click once & image rotates indefinitely. click again, image stops, click again, restarts.
i found answer helpful in getting animation continue: rotate view 360 degrees indefinitely in swift?
however, i'm unclear on how stop things. i've implemented code below & seems work, curious if proper way stop animation, or if there another, preferred method. - rotation continues until finishing, i'm wondering if can freeze rotation @ location when button pressed (i've tried .removeallanimations() in second attempt below, doesn't seem work @ all.
@iboutlet weak var imageview: uiimageview! var stoprotation = true func rotateview(targetview: uiview, duration: double = 1.0) { if !stoprotation { uiview.animate(withduration: duration, delay: 0.0, options: .curvelinear, animations: { targetview.transform = targetview.transform.rotated(by: cgfloat(double.pi)) }) { finished in self.rotateview(targetview: targetview, duration: duration) } } } @ibaction func spinpressed(_ sender: uibutton) { stoprotation = !stoprotation if !stoprotation { rotateview(targetview: imageview) } }
this work. wondering if it'd possible stop animation mid-spin. way it's set up, animation goes full 180 degrees before stopping. i've tried adding removeanimation in spinpressed action, , getting rid of stoprotation check inside rotateview, doesn't seem work – rotation continues & gets faster if spinpressed pressed again (see below):
@iboutlet weak var imageview: uiimageview! var stoprotation = true func rotateview(targetview: uiview, duration: double = 1.0) { uiview.animate(withduration: duration, delay: 0.0, options: .curvelinear, animations: { targetview.transform = targetview.transform.rotated(by: cgfloat(double.pi)) }) { finished in self.rotateview(targetview: targetview, duration: duration) } } @ibaction func spinpressed(_ sender: uibutton) { stoprotation = !stoprotation if stoprotation { imageview.layer.removeallanimations() } else { rotateview(targetview: imageview) } }
a confirm if first approach sound welcome. , if there way stop rotation mid-spin, that'd welcome (as setting me straight on flawed thinking on removeallanimations).
thanks! jg
there couple of ways you're asking about:
if supporting ios 10+, can use
uiviewpropertyanimator
, animations can pause , restart (resuming paused):private var animator: uiviewpropertyanimator? @ibaction func didtapbutton(_ sender: any) { guard let animator = animator else { createanimation() return } if animator.isrunning { animator.pauseanimation() } else { animator.startanimation() } } /// create , start 360 degree animation /// /// fire off animation when 1 360° rotation finishes. private func createanimation() { animator = uiviewpropertyanimator.runningpropertyanimator(withduration: 4, delay: 0, options: .curvelinear, animations: { uiview.animatekeyframes(withduration: 4, delay: 0, animations: { uiview.addkeyframe(withrelativestarttime: 0, relativeduration: 1.0 / 3.0) { self.animatedview.transform = .init(rotationangle: .pi * 2 * 1 / 3) } uiview.addkeyframe(withrelativestarttime: 1.0 / 3.0, relativeduration: 1.0 / 3.0) { self.animatedview.transform = .init(rotationangle: .pi * 2 * 2 / 3) } uiview.addkeyframe(withrelativestarttime: 2.0 / 3.0, relativeduration: 1.0 / 3.0) { self.animatedview.transform = .identity } }) }, completion: { [weak self] _ in self?.createanimation() }) }
you can alternatively use uikit dynamics rotate item. can remove
uidynamicitembehavior
performing rotation , stops was. automatically leaves viewtransform
was. then, resume rotation, adduidynamicitembehavior
rotation again:private lazy var animator: uidynamicanimator = uidynamicanimator(referenceview: view) private var rotate: uidynamicitembehavior? @ibaction func didtapbutton(_ sender: any) { if let rotate = rotate { animator.removebehavior(rotate) self.rotate = nil } else { rotate = uidynamicitembehavior(items: [animatedview]) rotate?.allowsrotation = true rotate?.angularresistance = 0 rotate?.addangularvelocity(1, for: animatedview) animator.addbehavior(rotate!) } }
this doesn't let control speed of rotation in terms of time, rather it’s dictated
angularvelocity
, it's nice simple approach (and supports ios 7.0 , later).the old-school approach stopping animation , leaving stopped capture
presentationlayer
of animation (which shows mid-flight). can grab current state, stop animation, , set transformpresentationlayer
reported.private var isanimating = false @ibaction func didtapbutton(_ sender: any) { if isanimating { let transform = animatedview.layer.presentation()!.transform animatedview.layer.removeallanimations() animatedview.layer.transform = transform } else { let rotate = cabasicanimation(keypath: "transform.rotation") rotate.byvalue = 2 * cgfloat.pi rotate.duration = 4 rotate.repeatcount = .greatestfinitemagnitude animatedview.layer.add(rotate, forkey: nil) } isanimating = !isanimating }
if want use
uiview
block based animation, have capture angle @ stopped animation, know restart animation. trick grabm12
,m11
ofcatransform3d
:angle = atan2(transform.m12, transform.m11)
thus, yields:
private var angle: cgfloat = 0 private var isanimating = false @ibaction func didtapbutton(_ sender: any) { if isanimating { let transform = animatedview.layer.presentation()!.transform angle = atan2(transform.m12, transform.m11) animatedview.layer.removeallanimations() animatedview.layer.transform = transform } else { uiview.animate(withduration: 4, delay: 0, options: .curvelinear, animations: { uiview.animatekeyframes(withduration: 4, delay: 0, options: .repeat, animations: { uiview.addkeyframe(withrelativestarttime: 0, relativeduration: 1.0 / 3.0) { self.animatedview.transform = .init(rotationangle: self.angle + .pi * 2 * 1 / 3) } uiview.addkeyframe(withrelativestarttime: 1.0 / 3.0, relativeduration: 1.0 / 3.0) { self.animatedview.transform = .init(rotationangle: self.angle + .pi * 2 * 2 / 3) } uiview.addkeyframe(withrelativestarttime: 2.0 / 3.0, relativeduration: 1.0 / 3.0) { self.animatedview.transform = .init(rotationangle: self.angle) } }) }) } isanimating = !isanimating }
you can rotate object using
cadisplaylink
updates angle calculated value. stopping rotation simple invalidating display link, thereby leaving when stopped. can resume animation adding display link runloop.this sort of technique gives great deal of control, least elegant of approaches.
Comments
Post a Comment