I hope this is a simple question. If I have a UINavigationController and I push a new view controller onto the stack with an animated transition, how can I detect when the animation has finished and the new view controller is on screen?
I have a few scenarios where I need to push a new controller that then has to do a long-running operation. I'd like to push the new view first so there's something on screen before I start blocking the main thread for a long time. If I do the push immediately followed by my long-running task the view won't show up until after both are done of course and the main thread is able to process events again.
So, what I'd like to do be able to detect in the new controller once the animation is done and the view is on screen, and then start the task.
You can check if the animation is complete by looking at the normalizedTime property of the Animator's AnimatorStateInfo: if (pc_anim.GetCurrentAnimatorStateInfo (0).IsName ("attack") && pc_anim.GetCurrentAnimatorStateInfo (0).normalizedTime >= 1.0f) { pc_atttacking = false; pc_anim.SetBool ("attack", false); }
There is no "StateEnded" or "TransitionEnded" event for now on the mecanim system... So the only way to do that is to pull the "CurrentAnimatorStateInfo" and check if you enter the state/leave it. if(this.animator.GetCurrentAnimatorStateInfo(0).IsName("YourAnimationName")) { // Avoid any reload.
I believe you can edit your animator states or transitions (I forget which; possibly both) to have events, causing them to call specific functions in your code when they reach a particular point. Chocolade likes this.
The above solution can be slightly tweaked to account for animations done with keyframes. Like transitions have the transitionend event, animations have the animationend event. We’ll take the whichtransitionEvent function and swap out instances of transition for animation (case sensitive).
+1 to @DHamrick's recommendation for not blocking the main thread at all.
To answer the original question, you can detect viewController changes in two places:
The viewController you just pushed will receive viewWillAppear: and viewDidAppear: messages. If you want to know when a specific viewController appears, implement these methods.
The navigationController:didShowViewController:animated:
method mentioned by @Mike Z is sent to the navigationController's delegate. You will need to assign an object to be that delegate in order to receive this message. You will then know every time a viewController appears.
Instead of blocking the main thread, you should look in to doing your long running operation inside of another thread or even better, using a GCD queue.
dispatch_queue_t backgroundQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, NULL);
dispatch_async(backgroundQueue, ^{
// Do your long running code
dispatch_async(dispatch_get_main_queue(), ^{
//Update your UI
});
});
You can put this code in your viewDidLoad:
so that you know your UI has already loaded. This also means you don't have to rely on the timing of the animation.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With