UIView.animate() issue - swift

I have a code, where UI elements are changed after user interaction. I use UIView.animate() to refresh UI. The code is pretty simple:
UIView.animate(withDuration: 1.0) {
self.fadeIn()
} completion: { _ in
UIView.animate(withDuration: 1.0) {
self.fadeOut()
}
}
Very important point is that total animation duration from beginning to the end should be 2 seconds. But sometimes, there are no UI changes in fadeIn(), and UIView.animate() just get immediately to completion block. But in that option I want to have a delay for duration time (1 sec), and only after that get to completion block to fadeOut. How I can force animate with duration even if it's nothing to animate in animation block? Thanks in advance!

Maybe don't call fadeOut() in the completion block, and just make sure that it's called with one second delay after the fadeIn() is called?
UIView.animate(withDuration: 1.0) {
self.fadeIn()
}
UIView.animate(withDuration: 1.0, delay: 1.0) {
self.fadeOut()
}

Related

Animation doesn't repeat correctly in Swift

#objc private func updateCountdownLabel(_ notification: NSNotification) {
self.progressWidthAnchor.constant = 0
if let timeRemaining = notification.userInfo?["timeRemaining"] as? Int {
self.secondsRemaining = timeRemaining
self.animateProgress(width: 100, duration: 10)
}
}
private func animateProgress(width: CGFloat, duration: Int) {
self.layoutIfNeeded()
UIView.animate(withDuration: TimeInterval(duration),
delay: TimeInterval(LocalConstants.animationDelayDuration),
options: .curveLinear) {
self.progressWidthAnchor.constant = width
self.layoutIfNeeded()
}
}
I'm having a weird issue with my animation. At the moment I am trying to animate a bar decreasing/increasing but I need it to repeat every second.
For some reason it doesn't repeat the animation, the constraint jumps outside the view.
It works the first time, but the second time it doesn't work as you'd expect.
No constraint warnings are thrown.
Moving code around,
updating the layoutIfNeeded to various locations
The cause of my animation not working correctly was due to
removeAllAnimations() not being called prior to the next UIView.animate code blo
Figured it out
Calling removeAllAnimations before executing the code again fixed my issue

Finish some animations of UIViewPropertyAnimator earlier

I search for a way to add some animations to an UIViewPropertyAnimator which finishing earlier then others.
UIViewPropertyAnimator have for example a method where you can add animations with a delay
animator.addAnimations(animation: (()-> Void), delayFactor: CGFloat)
so the animation starts at 50% of the duration at a delayFactor of 0.5.
I search for something like
animator.addAnimations(animation: (()->Void), realtiveDuration: CGFloat)
so the animation ends after 50% of the duration at a relativeDurationof 0.5.
After some research I found a solution by using
animator.addAnimations {
UIView.animateKeyframes(withDuration: duration, delay: 0.0, animations: {
UIView.addKeyframe(withRelativeStartTime: 0.0, relativeDuration: 0.3) {
view.alpha = 0.0
}
})
}
to archive this behavior.
Problem is, I want also use UIPercentDrivenInteractiveTransition to scrub through the animation.
Sadly this is not working with that animateKeyframes methods.
Have somebody another solution to archive this behavior?

Delay in DispatchQueue.main.async

I have a design like below flow. I need to set delay between 5th and 6th steps for 0.3second. I tried below options but couldn't get any result.
My question is, how can I achieve this?
Note: 13seconds for see the animation.
Flow
Task Handler // for webService request
Closure Handler // for trigger ViewController
DispatchQueue.main.async // for Update UI
First Animation
Second Animation
Navigation to next screen
Test 1
Timer.scheduledTimer(withTimeInterval: 13, repeats: false, block: {})
Test 2
UIView.animate(withDuration: 13, animations: {
// nothing should be happened
self.ivSuccessMark.alpha = 0.99 // for dummy animation diff
}, completion: { (completion) in
// navigation
})
Test 3
perform(_:with:afterDelay:)
Try this one
UIView.animate(withDuration: 0.5, delay: 0.3, options: [.repeat, .curveEaseOut, .autoreverse], animations: {
// animation stuff
}, completion: { _ in
// do stuff once animation is complete
})
Try this one I hope it help you(Up to 4 seconds Stop all Action in view)
DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(4), execute: {
// Put your code which should be executed with a delay here
})

UIView Block-Animation Completion Always True

I have had an animation that I have recently caught to be not working
//time is a variable used in my code
UIView.animate(withDuration: time, delay: 0, options: [.curveLinear, .allowUserInteraction], animations: {
//this class is ofType UIScrollView
self.setContentOffset(CGPoint(x: self.contentSize.width-self.frame.width, y: 0), animated: false)
//Completion Handler
}, completion: { finished in
//It's always true, not sure a way to fix this
if(finished ) {
But, later in my code, I have a method to remove certain animations, specifically from this scrollView.
self.layer.removeAllAnimations()
It gets called, and I would assume it is suppose to make the above
if(finished ) { //Here
return false, therefore it should not go inside the finished. But, finished is ALWAYS true. Whether I cancel this animation, continue the animation, doesn't matter what I do to the animation, the completion handler is always true. Any tips on this matter?
Your call to setContentOffset specifies animated:false, if that is the only property you are changing, then there are no actual animations going on, so the call to .animate(...) will always complete with a value of true. If you set this property (or another one) with animated:true and your duration is too short for the animation to finish, then it could complete with a false value for the parameter.

animateWithDuration Animates Once But Not Twice

I have this piece of animation in a switch statement that runs and does what it's supposed to. The animation is called successfully every twenty seconds inside a timer. However, the animation does not happen any time but the first time. In my code below you will see println statements that I used to try and highlight the problem. Each println statement prints but no animation happens. What is it that I'm missing?
func popAnimation(indexNumber: Int) {
let image = self.overlayImageView[indexNumber]
image.hidden = false
image.alpha = 0.0
UIView.animateWithDuration(0.75, delay: 0.0, usingSpringWithDamping: 0.5, initialSpringVelocity: 1.0, options: nil, animations: { () -> Void in
println("animation ran")
println(image.alpha)
image.image = UIImage(named: "lowCountOverlay")
image.alpha = 1.0
}, completion: { (Bool) -> Void in
UIView.animateWithDuration(0.75, delay: 0.0, usingSpringWithDamping: 0.5, initialSpringVelocity: 1.0, options: nil, animations: { () -> Void in
println("animation 2 ran")
println(image.alpha)
image.alpha = 0.0
}, completion: { (Bool) -> Void in
println("animation 3 ran")
image.hidden = true
})
})
}
UPDATE: If I remove image.hidden from the second completion block it works fine and animates repeatedly. Which is interesting because if it's not hidden it should be covering other interactive content in the view, but it's not. The other content is perfectly accessible in simulator. The UIImageView image is definitely the top layer in Storyboard.
I could be off, but it seems like you're running from an NSTimer which may or may not be in the main thread.
UI updates need to take place in the main thread, try wrapping the completion handler in a GCD dispatch directed to the main thread like the following and see if that helps? Maybe even force the whole thing to the main thread.
UIView.animateWithDuration(0.75, delay: 0.0, usingSpringWithDamping: 0.5, initialSpringVelocity: 1.0, options: nil, animations: { () -> Void in
// First animation
}, , completion: { (Bool) -> Void in
dispatch_async(dispatch_get_main_queue(),{
// second animation
})
})
I think you need to look for your bug elsewhere. You don't seem to be missing anything in the codeā€”as I'm able to run the animation repeatedly without any issues. I've copied your code as-is to a separate project and the animation works every time.
Try replacing:
let image = self.overlayImageView[indexNumber]
image.hidden = false
image.alpha = 0.0
with
let image = self.overlayImageView[indexNumber]
image.superview.bringSubviewToFront(image)
image.hidden = false
image.alpha = 0.0