Wait for Swift animation to complete before executing code
You can use a CATransaction
to call a completion block after the animation completes.
func shakeView(iv: UIImageView){
CATransaction.begin()
CATransaction.setCompletionBlock({
iv.hidden = true
})
var shake:CABasicAnimation = CABasicAnimation(keyPath: "position")
shake.duration = 0.1
shake.repeatCount = 21
shake.autoreverses = true
var from_point:CGPoint = CGPointMake(iv.center.x - 5, iv.center.y)
var from_value:NSValue = NSValue(CGPoint: from_point)
var to_point:CGPoint = CGPointMake(iv.center.x + 5, iv.center.y)
var to_value:NSValue = NSValue(CGPoint: to_point)
shake.fromValue = from_value
shake.toValue = to_value
iv.layer.addAnimation(shake, forKey: "position")
CATransaction.commit()
}
Or you can also group both animations together in a CATransaction
as follows.
func shakeView(iv: UIImageView){
var shake:CABasicAnimation = CABasicAnimation(keyPath: "position")
shake.duration = 0.1
shake.repeatCount = 21
shake.autoreverses = true
var from_point:CGPoint = CGPointMake(iv.center.x - 5, iv.center.y)
var from_value:NSValue = NSValue(CGPoint: from_point)
var to_point:CGPoint = CGPointMake(iv.center.x + 5, iv.center.y)
var to_value:NSValue = NSValue(CGPoint: to_point)
shake.fromValue = from_value
shake.toValue = to_value
iv.layer.addAnimation(shake, forKey: "position")
}
override func viewDidLoad() {
CATransaction.begin()
CATransaction.setCompletionBlock({
self.image1.hidden = true
self.image2.hidden = true
})
shakeView(image1)
shakeView(image)
CATransaction.commit()
}
Wait until animation finishes before moving on Swift
That is what the completion handlers are for. Place the next animation inside the completion of the previous one.
Drop this in a playground. If you split it up in functions you can keep it readable. But this will be broken next Wednesday.
var view = UIView(frame: CGRect(x: 0, y: 0, width: 10, height: 10))
view.backgroundColor = UIColor.blackColor()
func animationOne() {
UIView.animateWithDuration(1.0,
delay: 0.0,
options: nil,
animations: {view.alpha = 1},
completion: {finished in animationTwo()})
}
func animationTwo() {
UIView.animateWithDuration(1.0,
delay: 0.0,
options: nil,
animations: {view.alpha = 0},
completion: {finished in animationThree()})
}
func animationThree() {
UIView.animateWithDuration(1.0,
delay: 0.0,
options: nil,
animations: {view.alpha = 1},
completion: {finished in print("done")})
}
animationOne()
It appears swift2 doesn't like it when the options are nil:
options are now option sets which are arrays. nil has become []
UIView.animateWithDuration(2.0, delay: 0.0, options: [], animations: { () -> Void in
self.view.alpha = 1.0
}, completion: { (finished: Bool) -> Void in
// next animation
})
How to wait until an animation is finished in Swift?
If you want to use the completion
block at the end of the animation, you should use constructs with the delay
and options
arguments as well.
UIView.animateWithDuration(0.3, delay: 0.0, options: UIViewAnimationOptions.CurveLinear, animations: {
// put here the code you would like to animate
self.IMGVIEWcat.center = CGPointMake(self.IMGVIEWcat.center.x + 2, self.IMGVIEWcat.center.y)
}, completion: {(finished:Bool) in
// the code you put here will be compiled once the animation finishes
QueryInformations()
})
Wait until task completed before running it again
With that for
loop, you're essentially setting up all four animations to run at the same time. If you make that animation code a function, you can call it recursively from the completion block for your white animation:
func animateBackground(times: Int) {
if times == 0 { return }
let blackAnimation = { self.view.backgroundColor = UIColor.blackColor() }
let whiteAnimation = { self.view.backgroundColor = UIColor.whiteColor() }
UIView.animateWithDuration(0.33333, delay: 0.333333, options: UIViewAnimationOptions.CurveEaseIn, animations: blackAnimation) {
completedBlack in // completion block 1
UIView.animateWithDuration(0.333333, animations: whiteAnimation) {
completedWhite in // completion block 2
self.animateBackground(times - 1)
}
}
}
And the initial call looks like:
animateBackground(4)
Swift: Running code prevents other code until completed
I am calling a function which creates several arrays of UIImage objects and takes 4-5 seconds to complete
You absolutely cannot run that on the main thread. A refresh frame is 1/120 second on a modern device. You must not cause frames to be lost. You are blocking user interaction while that happens. The screen will be frozen and the Watchdog process will kill your app dead on the spot. (Unfortunately the Simulator doesn't show you that, nor does running from Xcode on the device; but if you run the app on the device, stop it, and run it from the device rather than from Xcode, you'll see what I mean.)
The issue is that the completion block doesn't run 0.8 seconds later - it doesn't run until AFTER the previous code completes.
As you've surmised, there's a re-entrancy problem here. You need to get off the main thread to allow the completion block to happen. I'm actually surprised that the animation even starts (does it?). Nothing at all can happen while you are still running code on the main thread.
You could call async_after
with a short delay to hop off the main thread momentarily and then re-enter it, long enough to let a refresh frame happen, so the system can do some work. But that would just be a way of letting the animation start. Once you're back on the main thread, you'll be blocking it again.
As for speed: a QOS of .background
is the slowest choice, so you're getting what you asked for. In general, though, don't use the global queues; make your own queue. I can't imagine what you're doing that can take so long, and maybe that is what you should really be asking about, but you absolutely cannot do it on the main thread.
Create a waiting queue for executing an animation
You may try to make your animation queue as like below example
var results = [Int]()
var isAnimating = false
private func subscribeToNewBLock() {
DispatchQueue.global(qos:.userInteractive).async {
watchForNewBlock() {result in
switch result {
case .Failure:
return
case .Success(let result):
//Call your UI operations in main thread
DispatchQueue.main.async {
self.results.append(result)
//Animation function
self.moveBlocksDown()
//Recursive call to keep listening for new blocks
self.subscribeToNewBLock()
}
}
}
}
}
private func moveBlocksDown() {
guard isAnimating == false && results.count > 0 else {
return
}
self.moveBlocksDown(blockNumber: results.first!)
}
private func moveBlocksDown(blockNumber:Int){
isAnimating = true
UIView.animate(withDuration: 2.0, animations: {
//Animation code goes here
}) { (completed) in
if completed{
//Add follwing code in place of animation completed(May be in completion handler)
self.isAnimating = false
self.results = self.results.filter{$0 != blockNumber} //Remove already animated blockNumber
self.moveBlocksDown() //Call moveBlocksDown function to check if anything pending in queue
}
}
}
How do i wait until an animation in a different class is finished before continuing?
You can push after the animation is done
[UIView animateWithDuration:0.8
animations:^{
// Animation
}
completion:^(BOOL finished){
// PushView
}];
for < iOS 4+ take a look at setAnimationDidStopSelector
or
[self performSelector:@selector(metohodToPushView) withObject:nil afterDelay:0.8];
Related Topics
How to List (Almost) All Emojis in Swift for iOS 8 Without Using Any Form of Lookup Tables
Calayer - Cabasicanimation Not Scaling Around Center/Anchorpoint
How to Change the App Store's Large Icon on Itunes Connect
_Unused Flag Behavior/Usage (Gcc with Objective-C)
How to Wait for a Function to End on iOS/Swift, Before Starting the Second One
Change the Listing Order of the View Controllers in Xcode Storyboard
iOS 8+ Framework with Nested Embedded Framework
iOS Searchdisplaycontroller Prevents Tableview from Background Color Change (Background Stays Gray)
How to Get Selected Text from Uitextfield in Iphone
Peripheral and Central at the Same Time on iOS
How to Implement Uitableview's Swipe to Delete for Uicollectionview
Ios/Ipados Safari Push API Support
Swift: Natively Detect If App Has Crashed
Add a Scrollview to Existing View
To Change the Color of Unselected Uitabbar Icon in iOS 7
Cannot Run Application on Simulator After Installing Xcode 11 - Cfbundleversion Error
How to Play a Video from Either a Local or a Server Url in iOS