Navigation Bar gets funky if Swipe Gesture and Bac

2019-09-21 05:38发布

问题:

Today one of our testers showed me that if he presses the back button of a navigation bar while swiping back creates a quite interesting behavior of the navigation bar:

if we are on the third view controller and perform this than go back to the top, than again go in one level and click the back button on most cases the navigation bar will not disappear if it should, or it maybe will not animate. Or not appear in deeper levels. Or deactivate the back button although not being in the top view controller.

Sometimes this messages is printed to the console:

Finishing up a navigation transition in an unexpected state. Navigation Bar subview tree might get corrupted.

It turns out that it must be in Apples classes, as I was able to reproduce it with plain classes. The code is on GitHub. You will have to run the app on the phone to perform gesture and button tap at once.

I also prepared a video.

How do I fix it?

回答1:

To fix this I disable user interactions for the navigationsbar. To do so, I subclass UINavigationViewController and use Key-Value-Observing to detect the state of the gesture recognizer.

#import "NavigationViewController.h"


@interface NavigationViewController ()
@end

@implementation NavigationViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    [self.interactivePopGestureRecognizer addObserver:self
                                           forKeyPath:@"state"
                                              options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld)
                                              context:NULL];
}




- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary *)change
                       context:(void *)context
{
    if ([keyPath isEqual:@"state"]) {
        [self recognizer:object
            changedState:[change[@"new"] integerValue]
                oldState:[change[@"old"] integerValue]];

    } else {
        [super observeValueForKeyPath:keyPath
                             ofObject:object
                               change:change
                              context:context];
    }
}

- (void)recognizer:(UIGestureRecognizer *)recognizer
      changedState:(UIGestureRecognizerState)newState
          oldState:(UIGestureRecognizerState)oldState
{
    switch (newState) {
        case UIGestureRecognizerStateEnded:
        case UIGestureRecognizerStateCancelled:
        case UIGestureRecognizerStateFailed:
            [self.navigationBar setUserInteractionEnabled:YES]; break;

        case UIGestureRecognizerStateBegan:
            [self.navigationBar setUserInteractionEnabled:NO]; break;

        default:
            break;
    }
}


- (void)dealloc
{
    [self.interactivePopGestureRecognizer removeObserver:self forKeyPath:@"state"];
}

@end

You will find the fixed code on GitHub as-well.



回答2:

Suppose you have a navigation controller with view controllers A --> B --> C.

The problem occurs when you are on C, swipe back to B, and touch the Back button on B before lifting your swiping fingers.

To prevent this:

In B viewDidDisappear:

    navigationItem.hidesBackButton = true

In B viewDidAppear:

    navigationItem.hidesBackButton = false

This has the effect of preventing touches on B's Back button until the swipe is complete.