Stopping and Resetting a CGAffineTransform - swift

I'm trying to write code where when a trigger happens an instance of UIImageView creates a slow "growing" effect with a CGAffineTransform running for 20 seconds.
The issue happens when the trigger happens again before the first transform has completed. Instead of the image resetting back to it's original size, it shrinks exponentially, depending on when the trigger happened during the first transformation.
Here is my current code:
func changeCategoryImage() {
self.categoryPanoramaImageView.transform = CGAffineTransform.identity
UIView.transition(with: self.categoryPanoramaImageView, duration: 0.4, options: .transitionCrossDissolve, animations: {
self.categoryPanoramaImageView.image = self.newPanoramaImage
}) { (done) in
UIView.animate(withDuration: 10, animations: {
self.categoryPanoramaImageView.transform = CGAffineTransform(scaleX: 5.0, y: 5.0)
})
}
}
I was under the impression that CGAffineTransform.identity would reset the image back to it's original size. This is seems to not be the case.
How can I halt and reset the current running transformation in order to start a new one?

I was under the impression that CGAffineTransform.identity would reset the image back to it's original size. This is seems to not be the case.
Correct. It isn't the case. That's not how animations work. You are making a direct change to the underlying view (i.e. the model layer), but the animation is still present (i.e. the presentation layer).
Thus, you are setting the view's transform to the identity, but you are not removing the existing animation. And animations are additive by default. Hence the result you are observing.
Try inserting
self.categoryPanoramaImageView.layer.removeAllAnimations()
as the first line of your function. That way, you will remove the animation and start over from the identity.

Related

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

Pre-Emptive Swift Animation Object Move - Confusing

Good day,
I have a very simple animation function, that drops a button by 200.
However, I discover that before the animation begins, the object is moved up (-) by 200! Therefore, after the animation, the button is back where it started.
I tried to set self.button1.center = self.view.center in viewDidAppear before calling the function.
func dropStep(){
UIView.animate(withDuration: 6, animations: {
self.button1.center.y += 200
}, completion: nil)
}
I expected the animation to start from where it is intended (at the center of the view) and not pre-emptively shifted up by 200 points.
Try,
func dropStep(){
button1.center = self.view.center
UIView.animate(withDuration: 6, animations: {
self.button1.center.y += 200
}, completion: nil)
}
Okay, so I could not genuinely find the reason behind this.
However, this was an app that was used and reused to learn animations, so maybe somehow the object referencing outlets or some sort of hidden reference was messed up, such that the app behaved funny.
Therefore, I created a new app with virtually the same code, and it behaved as expected. Thanks anyway for your help.

Can't access the current position of UIImageView

I'v read many questions about this topic but their outdated or I can't understand them. This is the animation:
UIView.animate(withDuration: 20, delay: 0, options: [.curveLinear], animations: {
self.SubmarineImageView.transform = CGAffineTransform(translationX: (-self.frame.width - 80), y: CGFloat(Int(arc4random_uniform(UInt32(self.size.height / 2)))))
})
How do I access the position of the SubmarineImageView during the animation?
Also these images show that the emitter cant update because the position dose't change
Image 1
Image 2
To access SubmarineImageView properties during animation, you can check it's presentationLayer. It can return nil, so you have to unwrap it first. To get a position during animation you can do:
if let presentation = self.submarineImageView.layer.presentation() {
return presentation.position
} else {
return self.submarineImageView.layer.position
}
Also, I'm a bit worried that you try to simply assign this value to emitter's position. That won't work, as the presentation layer's position is changing during animation. If you are trying to make it animate with the submarine, you should also modify the emitter's position in the animation block or add the emitter as a sublayer of the submarineImageView.

Animate Auto Layout Constraints and Alpha on NSView

Swift 3, iOS 10, macOS 10.12.4
I am building an app that runs on both iOS and Mac. On the iOS side, I have successfully animated a UIView. When the user taps on something, a popup appears and animates into position. Here's my code inside the tap event:
self.popupConstraintX.constant = x
self.popupConstraintY.constant = y
UIView.animate(withDuration: 0.25 , delay: 0.0, options: .curveLinear, animations: {
self.graphPopup.alpha = 1.0
self.layoutIfNeeded()
}, completion:nil)
In this case, self refers to a UITableViewCell that holds the graphPopup.
I have built the same thing on the Mac side, but I'm trying to animate graphPopup which is now an NSView. Here's what I have so far inside my click event:
self.popupConstraintX.constant = x
self.popupConstraintY.constant = y
self.view.layoutSubtreeIfNeeded()
NSAnimationContext.runAnimationGroup({_ in
self.graphPopup.alphaValue = 1.0
//Indicate the duration of the animation
NSAnimationContext.current().duration = 0.25
NSAnimationContext.current().allowsImplicitAnimation = true
self.view.updateConstraints()
self.view.layoutSubtreeIfNeeded()
}, completionHandler:nil)
Here self refers to the containing NSViewController. Nothing animates--not the position or the alpha of graphPopup. It just appears and disappears like it's on an Atari in 1985.
Any idea what I'm doing wrong with my NSView animation?
Update
For posterity's sake, here is the working code as suggested by BJ (with a slight tweak to use the implicit animation context):
self.popupConstraintX.constant = x
self.popupConstraintY.constant = y
NSAnimationContext.runAnimationGroup({context in
context.duration = 0.25
context.allowsImplicitAnimation = true
self.graphPopup.alphaValue = 1.0
self.view.layoutSubtreeIfNeeded()
}, completionHandler:nil)
The modification of alphaValue is happening before you have turned on implicit animation, so the alpha will not be animated. It's unclear to me whether that was intentional or not.
The view is not animating to the position given by the popupConstraints, because you're not actually doing anything inside the animation block that would cause the view's frame to change. In order to trigger an implicit animation, you must not only update constraints; you must also ensure that the view's frame changes within the animation block. If you're using AutoLayout, this is usually done by calling layoutSubtreeIfNeeded.
However, because you updated the constraints and called layoutSubtreeIfNeeded() prior to the animation block, there are no additional frame changes left to make inside the block (unless there's something happening down in updateConstraints() which you haven't shown us.)
You should remove the first call to layoutSubtreeIfNeeded(), or if it's still necessary, put it above the popupConstraint modifications. Then, when you call layoutSubtreeIfNeeded() within the animation block, a new frame will be set based on those changed constraints, and you should see animation happening.
Swift 5
animator() is the key.
You can simply do like this:
NSAnimationContext.runAnimationGroup({ context in
//Indicate the duration of the animation
context.duration = 0.25
self.popupConstraintX.animator().constant = x
self.popupConstraintY.animator().constant = y
self.graphPopup.animator().alphaValue = 1.0
}, completionHandler:nil)

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.