Finish some animations of UIViewPropertyAnimator earlier - swift

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?

Related

UIView.animate() issue

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()
}

trying to change the background color in a fading manner

I'm trying to change the background color from an image to white in a fading manner. Here's the current code I have:
UIView.animate(withDuration: 1, delay: 0, animations: { () -> Void in self.answer.alpha = 0})
UIView.animate(withDuration: 1, delay: 0, animations: { () -> Void in self.continueLabel.alpha = 0})
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [unowned self] in self.view.backgroundColor = UIColor(displayP3Red: 255/255, green: 255/255, blue: 255/255, alpha: 1)}
As you can see I'm also having two other items fade out, which are working fine. Right now though I just have the background change to white with a delay of 0.5 seconds, which doesn't look as smooth as I'd like. Ideally I'd like to have all three (the two labels and the background) fade together, with delay 0 and duration 1. I tried setting the background to white with alpha = 0, and then changing the alpha to 1 in a fading manner but that did not work either. If anybody could please provide some insight I'd greatly appreciate it. Thanks!
Also, below I've included how I called the image background, just in case it's helpful:
UIGraphicsBeginImageContext(self.view.frame.size)
UIImage(named: "nyc")?.draw(in: self.view.bounds)
let image: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
self.view.backgroundColor = UIColor(patternImage: image)
Ideally I'd like to have all three (the two labels and the background) fade together, with delay 0 and duration 1
Then why didn't you do that? You animated the alphas; why didn't you animate the background color? I mean to say, it isn't going to animate unless you tell it to animate:
UIView.animate(withDuration: 1, delay: 0, animations: { self.view.backgroundColor = UIColor(displayP3Red: 255/255, green: 255/255, blue: 255/255, alpha: 1)})
By the way, did you know you're allowed to animate more than one view in one animations block? Plus you don't need an in line for a known function type. So the whole thing can be rewritten a lot more neatly.
UIView.animate(withDuration: 1, delay: 0, animations: {
self.answer.alpha = 0
self.continueLabel.alpha = 0
self.view.backgroundColor = // whatever
})
EDIT: I’m going to guess that the animation of the background color doesn't work for you because your background color is a pattern. In that case you will need to use trickery! You’ll need another view in front of the background that animates from clear color to white color. — On the other hand, maybe you should never have used a pattern image to start with. Personally I never use them. If I want a backdrop, I put an image view behind everything else. If you had done that, you could now fade the image view's alpha along with the others, revealing the empty white window and view behind everything.

Changing MKMapView Center coordinate at a different speed then its region.span

I'm trying to make my map view have a custom animation whenever an annotation on the map is selected.
This custom animation needs to consist of the map view's center coordinate changing at a different speed than the map view's span. (I want the map view to simultaneously quickly adjust its center then zoom in slowly after)
I've managed to somewhat accomplish this by performing the animations one after another like so:
MKMapView.animate(withDuration: 0.1, delay: 0.0, options: UIViewAnimationOptions.curveEaseOut, animations: {
self.homeMapView.centerCoordinate = region.center
}, completion: {
(value: Bool) in
MKMapView.animate(withDuration: 1.5, delay: 0.0, options: UIViewAnimationOptions.curveEaseOut, animations: {
self.homeMapView.region.span = region.span
}, completion: {
(value: Bool) in
print ("finished animation")
})
})
The problem is, this does not look smooth and I would ideally like both animations starting at the same time and running concurrently. This could be accomplished with a CAAnimation and the center coordinate value being additive while the span changes, but unfortunately I'm dealing with a MKMapView not a CALayer.
Any ideas or help would be much appreciated! Thanks!

Implementing basic animations with tvOS

I'm hoping someone may be able to help me understand why my tvOS animations are not running.
In my code I have a function like this:
func testAnimation() {
clockLabel.alpha = 0.0
UIView.animateWithDuration(1.0, animations: {
self.clockLabel.alpha = 1.0
})
}
I am calling testAnimation() from within my viewDidLoad(), but no animation ever seems to happen.
I've tested with a few different types of animations, from things like position to opacity, but it seems that no animation ever actually runs in the Simulator.
At this time, my app does not have a focus. All I'm trying to do is load a blank UIView with a label in the middle that fades in.
You're trying to animate your UILabel before it has been displayed. Move your animation from viewDidLoad() to viewDidAppear().
I can confirm this behaviour as this happens for me as well. What I did was setting a delay of 0.1 (afterDelay:), and it is "good enough"
On the other hand, what DID actually work was setting a transform for my views. E.g. (objc though):
CGAffineTransform scaleTransform = CGAffineTransformMakeScale(0.5,0.5);
[UIView animateWithDuration: 0.5 animations:^{
_myView.transform = scaleTransform;
} completion: nil]
Maybe it's a bug in tvOS
Try using UIView Animation, like this (Swift):
clockLabel.alpha = 0
UIView.animateWithDuration(0.5, delay: 0.0, options: .Linear, animations: {
self.clockLabel.alpha = 1.0
}, completion: { finished in
// you may add some code here to make another action right after the animation end, or just delete this comment :)
})
This works on our side.

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