I'm using a custom-made container view controller in my dictionary app. Basically, the container view controller contains a custom navigation bar on top (NOT a UINavigationBar
--just a UIView
with back and forward UIButtons
, a UISearchBar
, and a bookmark UIButton
at the right), and a tab bar controller at the bottom.
My problem is this: I use the back and forward buttons to push and pop view controllers in one of the tabs (a UINavigationController
) so the user can navigate through the dictionary browsing history. However, if I press the back or forward buttons too fast, I get this message in the log pane and some of the screens don't appear at all:
Unbalanced calls to begin/end appearance transitions for
<DefinitionViewController: 0x8e5d230>.
Looking around StackOverflow, I understood that this is because clicking on the back or forward buttons too fast calls the push/pop methods of the UINavigatonController
in the active tab, but it does not let the animation finish. https://stackoverflow.com/a/17440074/855680
Pushing or popping view controllers without the animations solves the problem, but I do want to keep the animations. How can I approach this problem? I looked at the UINavigationController
class reference to see if there are any delegate methods or properties that indicate that it's in the middle of an animation, but there doesn't seem to be any.
Fixed it myself. The solution was to create a property in my container view controller which indicates whether the UINavigationController
transition animations are still happening:
@property (nonatomic, getter = isStillAnimatingTransition) BOOL stillAnimatingTransition;
Now, for all the UIViewController
classes that I push into the UINavigationController
, I set this flag to YES
or NO
in each of the view controllers' viewWillDisappear
and viewDidAppear
methods, like this:
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
self.containerViewController.stillAnimatingTransition = NO;
}
- (void)viewWillDisappear:(BOOL)animated
{
self.containerViewController.stillAnimatingTransition = YES;
[super viewWillDisappear:animated];
}
And my container view controller only ever allows the execution of the back and forward buttons if the animation flag is set to NO
, like this:
- (void)backButtonClicked
{
if (!self.isStillAnimatingTransition) {
// Do whatever.
}
}
- (void)forwardButtonClicked
{
if (!self.isStillAnimatingTransition) {
// Do whatever.
}
}
Maybe you can take advantage of the UINavigationControllerDelegate class and handle the events there.
In your main class that holds the navigation controller, set the delegate to yourself and handle the interactions there.
i.e. in the .h file:
@interface yourClass : UIViewController <UINavigationControllerDelegate> {
UINavigationController *content;
}
and then in the .m file:
content = [[UINavigationController alloc] initWithRootViewController:yourRootViewController];
content.delegate = self;
After that you can listen to the transition events via the following functions, and set your animation flags accordingly.
- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
stillAnimatingTransition = NO;
}
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
stillAnimatingTransition = YES;
}
You can find more references about the delegate protocol from apple
https://developer.apple.com/library/ios/documentation/uikit/reference/UINavigationControllerDelegate_Protocol/Reference/Reference.html#//apple_ref/occ/intfm/UINavigationControllerDelegate/navigationController:willShowViewController:animated: