UINavigationController transition animations trigg

2019-08-22 00:13发布

问题:

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.

回答1:

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.
    }
}


回答2:

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: