Why is my button being removed from its superview before it completes its animation? - swift

I am trying to move an my button to a certain part of the screen [as a custom button class GameBlock] and then remove it from its superview. However, it is acting weird in the sense that it removes itself from the superview before it completes the animation and I have no clue why. May somebody please help me out? I appreciate it.
UIView.animate(withDuration: movementTime,
delay: 0.0,
options: [],
animations: {
movedBlock.center = gridCoord[hitBlock.x][hitBlock.y]
},
completion: nil)
if movedBlock.center == gridCoord[hitBlock.x][hitBlock.y] {
movedBlock.removeFromSuperview()
}

If I understand your problem, this should works. Your view will be removed after the animation. The completion closure will be called at the end of the animation.
UIView.animate(withDuration: movementTime,
delay: 0.0,
options: [],
animations: {
movedBlock.center = gridCoord[hitBlock.x][hitBlock.y]
},
completion: { _ in
if movedBlock.center == gridCoord[hitBlock.x][hitBlock.y] {
movedBlock.removeFromSuperview()
}
})

Related

Swift Animate duration not working in CGAffineTransform

When I am translating one view with an animation of 1 second it is not working, but when I am executing the transform.identity it works fine.
Here is my code:
func hideCarousel() {
UIView.animate(withDuration: 1, animations: {
self.carouselER.transform = CGAffineTransform(translationX: 0, y: 200)
})
}
func showCarousel() {
UIView.animate(withDuration: 1, animations: {
self.carouselER.transform = .identity
})
}
To solve this issue I forced the animation to run in the main thread. Every time that you have problems with the performance of your UI elements like your animations or updating your label texts, try forcing to run the UI change in the main thread.
DispatchQueue.main.async {
UIView.animate(withDuration: 1, animations: {
self.carouselER.transform = CGAffineTransform(translationX: 0, y: 200)
})
}
I have also faced that problem with one timer that updated a label, but in this issue I thought it was some kind of problem of the CGAffineTransform.

Overlapping UIView animation canceling the first completion block

I have two buttons to move views. When I press the first button it will move a view and the other will move it back.
The first animation has a completion block, but I want to prevent this block from executing when the user moves the view back before the completion is done.
I have tried using solutions given to other questions similar to this but it doesn't seem to stop the execution of the first completion block.
#IBOutlet func moveView(sender: AnyObject) {
UIView.animateWithDuration(0.5, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 0, options: .AllowUserInteraction, animations: { /* animation */ }) { _ in /* completion */ }
}
#IBOutlet func moveBack(sender: AnyObject) {
UIView.animateWithDuration(0.0, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 0, options: .BeginFromCurrentState, animations: { /* animation */ }) { _ in /* completion */ }
}
I also tried removing all the animations and the completion parameter will be false but the animation will just jump to the end and animate from there.
Any ideas is very much appreciated.
You may use boolean flag to achieve the desired functionality like as under:
var moveViewFlag = false
var moveBackFlag = false
#IBOutlet func moveView(sender: AnyObject) {
UIView.animateWithDuration(0.5, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 0, options: .AllowUserInteraction, animations: { /* animation */ }) { _ in /* completion */
//inside completion block make the moveBackFlag to true
moveBackFlag = true
}
}
#IBOutlet func moveBack(sender: AnyObject) {
UIView.animateWithDuration(0.0, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 0, options: .BeginFromCurrentState, animations: { /* animation */ }) { _ in /* completion */
//inside completion block make the moveBackFlag to true
moveViewFlag = true
if moveBackFlag{
//do whatever you want to do!
}
}
}
Hope this helps.
Than you should create a custom class and handle that inside the class to avoid cluttered flags.
After searching for a while I think I've found a solution. To get the completion block to return false, the animation has to be removed. But by doing so, the animation will jump to the "end state" so to prevent this I used:
object.layer.frame = object.layer.presentationLayer()!.frame
object.layer.removeAllAnimations()
From here I can start my new animation (moving back the object) without worrying about the completion block of the first animation. Information
I'm not sure if this is how it should be done or if there's a better way of doing this.

UIView animations in Swift

I am trying to execute following code.
UIView.animateWithDuration(5.0, animations: {
println("animations")
}, completion: { finished in
println("completion")
})
So Here completion block is called immediately before 5 sec animation duration. Don't know what is wrong here.
Please let me know for proper code.
You should add some view for animation:
UIView.animateWithDuration(5.0, animations: {
someView.alpha = 0
println("animations")
}, completion: { finished in
println("completion")
})
You don't have any animation sequence in your animations block, try animating your current view and check it'll work fine.
UIView.animateWithDuration(5.0, animations: {
self.view.alpha = 0.5;
print("animations")
}, completion: { finished in
self.view.alpha = 1.0;
print("completion")
})

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

Issue with flash animation with record button swift

I am trying to make a record button so when user click on it and it's start recording and I want to apply flash animation on that button and I found THIS post. And I convert that code into swift but it is not working and here is my swift code:
var buttonFlashing = false
#IBAction func record(sender: AnyObject) {
println("Button Tapped")
if !buttonFlashing {
startFlashingbutton()
} else {
stopFlashingbutton()
}
}
func startFlashingbutton() {
buttonFlashing = true
recordButton.alpha = 1
UIView.animateWithDuration(0.5 , delay: 0.0, options: UIViewAnimationOptions.CurveEaseInOut | UIViewAnimationOptions.Repeat | UIViewAnimationOptions.Autoreverse | UIViewAnimationOptions.AllowUserInteraction, animations: {
self.recordButton.alpha = 0
}, completion: {Bool in
})
}
func stopFlashingbutton() {
buttonFlashing = false
UIView.animateWithDuration(0.1, delay: 0.0, options: UIViewAnimationOptions.CurveEaseInOut | UIViewAnimationOptions.BeginFromCurrentState, animations: {
self.recordButton.alpha = 1
}, completion: {Bool in
})
}
It is printing "Button Tapped" for first time when I press button and animation takes place but when I press button again "Button Tapped" is not printing into console. And I can not stop animation.
I can not find what is wrong here.
The problem with your code is that the alpha is set to zero. So, OS disables the interaction when the alpha is zero or is hidden. The animation is applied to the layer of the button, and it already sets the alpha to the final value. So, you could set the alpha value to as low as 0.1 just to enable the user interaction and that should be fine.
You could do either one of the following options,
Set the alpha to 0.1
UIView.animateWithDuration(0.5 , delay: 0.0, options:
[
UIViewAnimationOptions.CurveEaseInOut,
UIViewAnimationOptions.Autoreverse,
UIViewAnimationOptions.Repeat,
UIViewAnimationOptions.AllowUserInteraction
],
animations: {
self.recordButton.alpha = 0.1
}, completion: {Bool in
})
Or, then set the color of the layer to clearColor which seems more sensible in your case.
UIView.animateWithDuration(0.5 , delay: 0.0, options:
[
UIViewAnimationOptions.CurveEaseInOut,
UIViewAnimationOptions.Autoreverse,
UIViewAnimationOptions.Repeat,
UIViewAnimationOptions.AllowUserInteraction
],
animations: {
self.recordButton.layer.backgroundColor = UIColor.clearColor().CGColor
}, completion: {Bool in
})