当UINavigationBar
发挥作用时,我在应用中的interactivePopGestureRecognizer
标题出现了一个奇怪的问题。我已经做了 demo app 来展示这个错误。
UINavigationController
。FirstViewController
隐藏了导航栏,interactivePopGestureRecognizer.enabled = NO;
Second
和ThirdViewController
可以看到导航栏并启用了popgesture。使用popgesture从第二个视图返回到第一个视图时会发生错误。如果您中途拉出第二个视图然后返回第二个视图,导航标题将显示“第二个视图”(如预期的那样)。但是当您转到第三个视图时,标题将不会更改为“第三个视图”。然后单击第三个视图的后退按钮,导航栏将变得混乱。
请查看我的演示应用。任何帮助解释为什么会发生这个错误将不胜感激。谢谢!
答案 0 :(得分:48)
首先,您的示例可以大大简化。您应该删除所有viewDidLoad
内容,因为它是一个完整的红色鲱鱼,只会使问题复杂化。在视图控制器的每次更改时,您都不应该使用pop手势识别器委托;并且关闭和打开弹出手势识别器与示例无关(默认情况下它处于打开状态,并且应该保留为此示例)。所以在所有三个视图控制器中删除这种东西:
- (void)viewDidLoad {
[super viewDidLoad];
if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) {
self.navigationController.interactivePopGestureRecognizer.enabled = NO;
self.navigationController.interactivePopGestureRecognizer.delegate = self;
}
}
(不要删除设置self.title
的代码,尽管通过在每个视图控制器的xib
文件中执行此操作可以使事情变得更简单。)
您还可以完全摆脱其他未使用的方法,例如init...
方法和内存警报方法。
顺便说一下,另一个问题是您忘记在super
的实施中致电viewWillAppear:
。你需要这样做。我认为这不会影响bug,但在开始尝试跟踪这些事情之前,最好遵守所有规则。
现在错误仍然存在,但我们有更简单的代码,因此我们可以开始解决问题。
那么问题的原因是什么?我认为理解它最明显的方法是实现流行手势的运作方式。这是一个交互式视图控制器转换动画。那是对的 - 它是一个动画。它的工作方式是弹出动画(从左侧滑动)附加到超视图层,但speed
为0,因此它实际上并没有运行。随着手势的进行,图层的timeOffset
会不断更新,以便相应的"框架#34;动画出现了。因此,看起来就像拖动视图一样,但你不是;你只是做一个手势,动画正在以相同的速度和相同的程度进行。我在这个答案中解释了这个机制:https://stackoverflow.com/a/22677298/341994
最重要的(注意这一部分),如果手势在中间放弃(几乎可以肯定),则决定手势是否超过一半完成,并基于这个,要么动画快速播放到最后(即speed
设置为3
),要么动画向后跑到(即{ {1}}设置为speed
)。
现在让我们谈谈这个bug。这里有两个并发症,你不小心撞到了:
当弹出动画和弹出手势开始时,即使视图最终未出现(因为这是交互式手势并且手势可能被取消),也会为前一个视图控制器调用-3
。如果您习惯于viewWillAppear:
始终跟随视图实际接管屏幕(并且viewWillAppear:
被调用),这可能是一个严重的问题,因为这这种情况可能不会发生。 (正如Apple在WWDC 2013视频中所说,"视图将出现"实际上意味着"视图可能出现&#34 ;.)
有一组辅助动画,即与导航栏相关的所有内容 - 标题的更改(它应该淡入视图),在这种情况下,不隐藏和隐藏之间的变化。运行时尝试使用滑动视图动画协调辅助动画集。但是当隐藏或显示栏时,你通过调用 no 动画来解决这个问题。
因此,正如您已经被告知的那样,一个解决方案是在整个代码中将viewDidAppear:
更改为animated:NO
。这样,导航栏的显示和隐藏就被命名为动画的一部分。因此,当手势被取消并且动画向后运行到开始时,导航的显示/隐藏也向后运行到开始 - 这两件事情现在保持协调。
但如果你真的不想做出改变呢?好吧,另一个解决方案是将animated:YES
改为viewWillAppear:
。正如我已经说过的那样,viewDidAppear:
在动画开始时被调用,即使手势没有完成,这也会导致事情失控。但只有在手势完成(未取消)和动画已经结束时才会调用viewWillAppear:
。
我更喜欢这两种解决方案中的哪一种?他们都不是!他们都强迫你做出你不想做出的改变。在我看来,真正的解决方案是使用转换协调器。
转换协调器是系统为此目的提供的对象,即检测我们是否参与了交互式转换,并根据其是否被取消而表现不同或不。
专注于viewDidAppear:
的OneViewController实现。这是事情变得混乱的地方。当您在TwoViewController中并从左侧开始平移手势时,OneViewController的viewWillAppear:
正在被调用。但是你取消了,放弃了手势而没有完成它。在这种情况下,您希望不在OneViewController viewWillAppear:
中执行您正在执行的操作。这就是转换协调员允许你做的。。
然后,这是OneViewController' s viewWillAppear:
的重写。这可以解决问题,而无需进行任何其他更改:
viewWillAppear:
答案 1 :(得分:6)
修复很简单,但我现在没有任何解释为什么会发生这种情况。
OneViewController中的一个将viewWillAppear
更改为
-(void)viewWillAppear:(BOOL)animated{
// [self.navigationController setNavigationBarHidden:YES animated:NO];
self.navigationController.navigationBar.hidden = YES;
}
并在第二个和第三个视图控制器上将其更改为,
-(void)viewWillAppear:(BOOL)animated{
//[self.navigationController setNavigationBarHidden:NO animated:NO];
self.navigationController.navigationBar.hidden = NO;
}
奇怪但是当我们直接使用UINavigationBar的隐藏属性时,这将解决问题。
答案 2 :(得分:1)
我不知道你是怎么做的“FirstViewController隐藏了导航栏。”
我有同样的问题,我通过替换
来修复它self.navigationController.navigationBarHidden = YES / NO;
通过
[self.navigationController setNavigationBarHidden:YES / NO animated:animated];
答案 3 :(得分:1)
我放弃尝试使用我自己的滑动识别器来弹出导航堆栈:
override func viewDidLoad() {
super.viewDidLoad()
// disable system swipe back gesture and add our own
navigationController?.interactivePopGestureRecognizer?.enabled = false
let swipeBackGestureRecognizer = UISwipeGestureRecognizer(target: self, action: "swipeBackAction:")
swipeBackGestureRecognizer.direction = UISwipeGestureRecognizerDirection.Right
tableView.addGestureRecognizer(swipeBackGestureRecognizer)
}
func swipeBackAction(sender: UISwipeGestureRecognizer) {
navigationController?.popViewControllerAnimated(true)
}
答案 4 :(得分:0)
这是为我解决的问题(斯威夫特)
第一个视图控制器:
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.setNavigationBarHidden(true, animated: animated)
}
第二和第三视图控制器:
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.setNavigationBarHidden(false, animated: animated)
}