Reputation: 1233
I am trying to create an animation where two line are faded out when the user drags a UIView and faded back in when user releases dragging.
Therefore I have two functions undrawLines
(called at pan gesture start) and redrawLines
(called at pan gesture end) which are called by my UIPanGestureRecognizer
action handler.
func undrawLines() {
line1.opacity = 0.0
line2.opacity = 0.0
line1.removeAllAnimations()
line2.removeAllAnimations()
let opacityLine = CABasicAnimation(keyPath: "opacity")
opacityLine.fromValue = 1.0
opacityLine.toValue = 0.0
opacityLine.duration = 0.15
line1.add(opacityLine, forKey: "disappearLine1")
line2.add(opacityLine, forKey: "disappearLine2")
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3, execute: {
mill.line1.removeFromSuperlayer()
mill.line2.removeFromSuperlayer()
})
}
func redrawLines() {
line1.opacity = 1.0
line2.opacity = 1.0
print("redraw")
line1.removeAllAnimations()
line2.removeAllAnimations()
self.layer.addSublayer(line1)
self.layer.addSublayer(line2)
let opacityLine = CABasicAnimation(keyPath: "opacity")
opacityLine.fromValue = 0.0
opacityLine.toValue = 1.0
opacityLine.duration = 0.15
line1.add(opacityMill, forKey: "appearLine1")
line2.add(opacityMill, forKey: "appearLine2")
}
The problem is that when redrawLines
gets called while the undrawLines
animation is still running, the lines show a strange behavior and opacity is 0.
Here is a demo, the first part shows how it should be, the second one shows the bug:
Upvotes: 0
Views: 111
Reputation: 4905
I believe your issue here is a race condition with your completion handler:
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3, execute: {
mill.line1.removeFromSuperlayer()
mill.line2.removeFromSuperlayer()
})
If your user releases, and therefore redrawLines
gets called before the 0.3 second timeout, this still gets called and removes the lines.
You probably want to keep a state flag that indicates the current intent and then check it in the asynchronous callback:
func undrawLines() {
self.linesHidden = true // update state
line1.opacity = 0.0
line2.opacity = 0.0
line1.removeAllAnimations()
line2.removeAllAnimations()
let opacityLine = CABasicAnimation(keyPath: "opacity")
opacityLine.fromValue = 1.0
opacityLine.toValue = 0.0
opacityLine.duration = 0.15
line1.add(opacityLine, forKey: "disappearLine1")
line2.add(opacityLine, forKey: "disappearLine2")
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3, execute: { [weak self] in
if self?.linesHidden == true { // check this is still what we want to do
mill.line1.removeFromSuperlayer()
mill.line2.removeFromSuperlayer()
}
})
}
func redrawLines() {
self.linesHidden = false // update state
line1.opacity = 1.0
line2.opacity = 1.0
print("redraw")
line1.removeAllAnimations()
line2.removeAllAnimations()
self.layer.addSublayer(line1)
self.layer.addSublayer(line2)
let opacityLine = CABasicAnimation(keyPath: "opacity")
opacityLine.fromValue = 0.0
opacityLine.toValue = 1.0
opacityLine.duration = 0.15
line1.add(opacityMill, forKey: "appearLine1")
line2.add(opacityMill, forKey: "appearLine2")
}
You'll clearly need to add the instance var linesHidden
to the class for this to work too :)
Upvotes: 1