UIView interpolation between 2 background images - swift

Let's say a UIView has a background image A, and I want to make an animation so A slowly turns into another image B.
how can I do this effect with images in background (png, for example)? I know that with just background colors, I can do this just by using UIView.animate

Look into using Core Image, particularly the 11 CICategoryTransition filter. Move your before/after CIImages into a CALayer, and animate.

You cannot have two images in the same time (unless you haven't two imageView). That is the way with one UIImageView:
UIView.animateWithDuration(1, delay: 0, options: UIViewAnimationOptions.TransitionCrossDissolve, animations: {
self.imageView.alpha = 0.0
}, completion: nil)
UIView.animateWithDuration(1, delay: 0, options: UIViewAnimationOptions.TransitionCrossDissolve, animations: {
self.imageView.alpha = 1.0
self.imageView.image = self.secondImage
}, completion: nil)

The contentsproperty of CALayer is animatable, and you can assign CGImages to it. An animated change could look like this:
CATransaction.begin()
CATransaction.animationDuration = 2.0;
theView.layer.contents = theNewImage.CGImage
CATransaction.commit();

Related

UIDatePicker's Change Year Arrow's Animation Transition

What is the animation transition used in the right-arrow image to the down-arrow image when changing the year in the UIDatePicker control? The animation smoothly rotates the start image to the end image like so:
I am trying to mimic this transition for an image in my code. In viewDidLoad(), I have:
myButton.setImage(UIImage(named: "rightArrow")?.withRenderingMode(.alwaysTemplate), for: .normal)
and then within the function invoked when that button is tapped, I do:
UIView.transition(with: myButton, duration: 0.6, options: .transitionCrossDissolve, animations: {
self.myButton.setImage(UIImage(named: "downArrow")?.withRenderingMode(.alwaysTemplate), for: .normal)
}, completion: nil)
I am using .transitionCrossDissolve here, but obviously it isn't the right transition. I tried each of the seven available transition options, but none of them behave like in UIDatePicker. Is there an easy way to create this "rotating" transition?
I finally figured it out. This will animate the rotation of the arrow:
UIView.transition(with: myButton, duration: 0.2, options: [], animations: {
self.myButton.transform = CGAffineTransform(rotationAngle: .pi/2)
}, completion: nil)
EDIT -- shorter solution:
UIView.animate(withDuration: 0.2) {
self.myButton.transform = CGAffineTransform(rotationAngle: .pi/2)
}

Setting the image on UIImageView changes the animation

I have a UIImageView that I wish to animate at the click of a button. But before the animation, I set its image. However, this image-setting messes up the animation. When I comment out the image-set, the animation works as expected.
var busFrame = self.newBusView.frame
let sourceFrame = self.buttonA.superview?.convert(self.buttonA.frame.origin, to: nil)
busFrame.origin.y = sourceFrame!.y
busFrame.origin.x = sourceFrame!.x
self.newBusView.frame = busFrame
//the following line messes up the animation
// self.newBusView.image = UIImage(named: "back_blue")
UIView.animate(withDuration: 2.0, delay: 0.3, options: .curveEaseOut, animations: {
busFrame = self.newBusView.frame
let desstinationFrame = self.buttonD.superview?.convert(self.buttonD.frame.origin, to: nil)
busFrame.origin.y = desstinationFrame!.y
busFrame.origin.x = desstinationFrame!.x
self.newBusView.frame = busFrame
}, completion: { finished in
print("Done!")
//same thing happening here
//self.newBusView.image = UIImage(named: "Background 1")
})
Also note that the buttons are in separate Stack views (hence the need for superview?.convert)
UPDATE:
So it was noticed that whenever I have a button.setImage() the positions go wrong (Even if the button has nothing to do with the animation). But instead of setImage, if I use button.imageView.image = something, then it has no side effects on animations. What is happening?
Expected:
Actual:
As we do not know the "expected" animation, I could not repeat the problem that you have. When I set same image again in the commented line, I get the same animation.
It may be about the area that the image covers. New image's ratio may be different than the default, therefore you see different animation although view's animation is same. You actually animate the view. Hence, you should focus on frames.
I believe that as #musakokcen mentioned your image have different size.
Try to use:
yourImageView.layer.masksToBounds = yes

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!

Changing UIButton alpha then doesn't recognize tap

I set up an interface very much like youtube in the sense that if a user presses on a UIView, I'll hide or show all of the subviews with an animation.
func setupControlViewVisiblity(playingVideo: Bool = false, duration: TimeInterval = 1.0, isExpanded: Bool = false){
var alpha:CGFloat = 1
if isShowingControls {
//If we're showing then we're about to hide everything so alpha is 0
alpha = 0
}
UIView.animate(withDuration: duration, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: .curveEaseOut, animations: {
self.pausePlayButton.alpha = alpha
self.backButton.alpha = alpha
self.infoLabel.alpha = alpha
self.fullScreenButton.alpha = alpha
self.currentTimeLabel.alpha = alpha
self.videoLengthLabel.alpha = alpha
self.videoSlider.alpha = alpha
}, completion:nil)
isShowingControls = !isShowingControls
}
The issue that I'm having is that if I call this method and I show the controls (isShowingControls is true), my UIButtons will not recognize the first tap. This literally only happens when the views alpha go from 0 to 1 then I try to tap a button.
I tried doing the animation on the main queue... doesn't work. If I change the alpha from 0 to 1.... wait a few seconds... then tap the button.. it works. How can I make it work right after the UIButtons alpha changed?
Thanks
EDIT: I guess it has to do with the animation duration because when I switch it down to 0.2 it works. How can I keep it a bit higher and get this to work?
By default UIVIew animations do not allow user interaction while the animation is animating.
Luckily there is an animation option allowUserInteraction. Not much more information but you can see it in documentation here. Also worth checking out all the options here.
Try this:
UIView.animate(withDuration: duration, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: [.curveEaseOut, .allowUserInteraction], animations: {
// Update the views
}, completion:nil)