I have a func to flash a view a number of times and execute closure at the end.
The following code with UIView.setAnimationRepeatCount works OK: animation occurs the requested number of times and afterEnd closure executes at the end.
func flashIt(repeated: Int, cycleTime: Double, delayed: Double = 5.0, afterEnd: (() -> Void)? = nil) {
if repeated < 0 { return }
let initAlpha = self.alpha
UIView.animate(withDuration: cycleTime,
delay: delayed,
options:[.allowUserInteraction, .curveEaseInOut, .autoreverse, .repeat],
animations: {
UIView.setAnimationRepeatCount(Float(repeated))
self.alpha = 0.1 // Not 0.0, to allow user interaction
},
completion: { (done: Bool) in
self.alpha = initAlpha
afterEnd?()
} )
}
To address UIView.setAnimationRepeatCount deprecation, I now try this (inspired by https://stackoverflow.com/questions/47496584/uiviewpropertyanimator-reverse-animation)
func flashIt(repeated: Int, cycleTime: Double, delayed: Double = 5.0, afterEnd: (() -> Void)? = nil) {
if repeated < 0 { return }
let initAlpha = self.alpha
let animator = UIViewPropertyAnimator(duration: cycleTime, curve: .linear) {
self.alpha = 0.1
}
animator.addCompletion { _ in
let reverseAnimator = UIViewPropertyAnimator(duration: cycleTime, curve: .linear) {
self.alpha = initAlpha
}
reverseAnimator.addCompletion { [self] _ in
flashIt(repeated: repeated-1, cycleTime: cycleTime, delayed: 0) // without delay here
}
reverseAnimator.startAnimation()
if repeated <= 1 { // 1 and not 0, otherwise an extra loop…
afterEnd?() // Not called
return
}
}
animator.startAnimation(afterDelay: delayed)
}
The flash works, but afterEnd closure is never called.
I've tried to call
if repeated <= 1 { // 1 and not 0, otherwise an extra loop…
afterEnd?() // Not called
return
}
in other places, to no avail.
What do I miss ?
Is there a better and simpler way to have a RepeatCount with UIViewPropertyAnimator ?
If anyone interested, here is the extension:
extension UIView {
// --------------------- flashIt -------------------------------------------------------------
// Description: Flash a view a repeated number of times, by changing alpha of the view ; each flash cycle lasts cycleTime
// Parameters
// repeated: Int number of flash cycles repeat : 0 = no repeat, just once. 1: repeat once (so 2 flashes)
// cycleTime: Double duration of a cycle
// delayed: Double wait before animation starts
// afterEnd: (() -> Void)? = nil Execute at the end of flashIt animation
// -------------------------------------------------------------------------------------------------
func flashIt(repeated: Int, cycleTime: Double, delayed: Double = 5.0, afterEnd: (() -> Void)? = nil) {
if repeated < 0 { return }
let initAlpha = self.alpha
let animator = UIViewPropertyAnimator(duration: cycleTime, curve: .linear) {
self.alpha = 0.1
}
animator.addCompletion { _ in
// reverse animation
let reverseAnimator = UIViewPropertyAnimator(duration: cycleTime, curve: .linear) {
self.alpha = initAlpha
}
reverseAnimator.addCompletion { [self] _ in
flashIt(repeated: repeated-1, cycleTime: cycleTime, delayed: 0, afterEnd: afterEnd) // loop, without delay
if repeated <= 0 {
afterEnd?() // Execute at the end of animation.
return
}
}
reverseAnimator.startAnimation()
}
animator.startAnimation(afterDelay: delayed)
}