如果同时触发滑动手势和后退按钮,导航栏会变得很奇怪
Navigation Bar gets funky if Swipe Gesture and Back Button are triggered at the same time
今天,我们的一位测试人员向我展示了如果他在向后滑动时按下导航栏的后退按钮,则会创建一个非常有趣的导航栏行为:
如果我们在第三个视图控制器上执行此操作而不是返回顶部,那么在大多数情况下再次进入一个级别并单击后退按钮导航栏不会消失,或者它可能不会动画。或者不出现在更深层次。或者停用后退按钮,尽管它不在顶视图控制器中。
有时,此消息会打印到控制台:
Finishing up a navigation transition in an unexpected state. Navigation Bar subview tree might get corrupted.
事实证明它一定是在 Apples 类 中,因为我能够用普通 类 重现它。代码在 GitHub 上。您必须 运行 phone 上的应用才能立即执行手势和按钮点击。
我也准备了一个video.
我该如何解决?
为了解决这个问题,我禁用了导航栏的用户交互。为此,我继承了 UINavigationViewController 并使用 Key-Value-Observing 来检测手势识别器的状态。
#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
您也可以在 GitHub 上找到固定代码。
假设您有一个带有视图控制器 A --> B --> C 的导航控制器。
当您在 C 上滑动返回 B,并在抬起滑动手指之前触摸 B 上的返回按钮时出现问题。
要防止这种情况:
在 B viewDidDisappear:
navigationItem.hidesBackButton = true
在 B viewDidAppear:
navigationItem.hidesBackButton = false
这具有防止触摸 B 的后退按钮直到滑动完成的效果。
今天,我们的一位测试人员向我展示了如果他在向后滑动时按下导航栏的后退按钮,则会创建一个非常有趣的导航栏行为:
如果我们在第三个视图控制器上执行此操作而不是返回顶部,那么在大多数情况下再次进入一个级别并单击后退按钮导航栏不会消失,或者它可能不会动画。或者不出现在更深层次。或者停用后退按钮,尽管它不在顶视图控制器中。
有时,此消息会打印到控制台:
Finishing up a navigation transition in an unexpected state. Navigation Bar subview tree might get corrupted.
事实证明它一定是在 Apples 类 中,因为我能够用普通 类 重现它。代码在 GitHub 上。您必须 运行 phone 上的应用才能立即执行手势和按钮点击。
我也准备了一个video.
我该如何解决?
为了解决这个问题,我禁用了导航栏的用户交互。为此,我继承了 UINavigationViewController 并使用 Key-Value-Observing 来检测手势识别器的状态。
#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
您也可以在 GitHub 上找到固定代码。
假设您有一个带有视图控制器 A --> B --> C 的导航控制器。
当您在 C 上滑动返回 B,并在抬起滑动手指之前触摸 B 上的返回按钮时出现问题。
要防止这种情况:
在 B viewDidDisappear:
navigationItem.hidesBackButton = true
在 B viewDidAppear:
navigationItem.hidesBackButton = false
这具有防止触摸 B 的后退按钮直到滑动完成的效果。