Answers:
更新:根据一些评论,原始答案中的解决方案似乎在iOS 8+中的某些情况下不起作用。如果没有更多详细信息,我无法证实确实如此。
对于那些在那种情况下的人,还有另一种选择。可以通过覆盖检测何时弹出视图控制器willMove(toParentViewController:)
。基本思想是在parent
is 时弹出视图控制器nil
。
请查看“实现Container View Controller”以了解更多详细信息。
从iOS 5开始,我发现处理这种情况的最简单方法是使用新方法- (BOOL)isMovingFromParentViewController
:
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
if (self.isMovingFromParentViewController) {
// Do your stuff here
}
}
- (BOOL)isMovingFromParentViewController
当您在导航堆栈中推入和弹出控制器时很有意义。
但是,如果要呈现模式视图控制器,则应- (BOOL)isBeingDismissed
改用:
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
if (self.isBeingDismissed) {
// Do your stuff here
}
}
如本问题所述,您可以结合使用以下两个属性:
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
if (self.isMovingFromParentViewController || self.isBeingDismissed) {
// Do your stuff here
}
}
其他解决方案依赖于的存在UINavigationBar
。相反,更喜欢我的方法,因为它使执行任务所需的任务与触发事件的操作(即按后退按钮)分离。
self.isMovingFromParentViewController
当我以编程方式弹出导航堆栈时popToRootViewControllerAnimated
- 具有TRUE值-无需触摸后退按钮。我应该否决你的答案吗?(对象说“在导航栏中按下了“后退”按钮”)
override func viewWillDisappear(animated: Bool) { super.viewWillDisappear(animated) if isMovingFromParentViewController(){ println("back button pressed") } }
-viewDidDisappear:
因为可能会得到一个-viewWillDisappear:
不带“ a”的-viewDidDisappear:
符号(例如,当您开始滑动以消除导航控制器项然后取消该滑动时。)
当点击后退按钮时会调用viewWillAppear()
和,而在其他时间也会调用它们。有关更多信息,请参见答案结尾。viewDidDisappear()
当借助willMoveToParentViewController(_:)
OR 将VC从其父级(NavigationController)中删除时,最好检测到后退按钮didMoveToParentViewController()
如果parent为零,则将视图控制器从导航堆栈中弹出并关闭。如果parent不为nil,则将其添加到堆栈中并显示。
// Objective-C
-(void)willMoveToParentViewController:(UIViewController *)parent {
[super willMoveToParentViewController:parent];
if (!parent){
// The back button was pressed or interactive gesture used
}
}
// Swift
override func willMove(toParent parent: UIViewController?) {
super.willMove(toParent: parent)
if parent == nil {
// The back button was pressed or interactive gesture used
}
}
换出willMove
了didMove
和检查self.parent做工作后视图控制器被驳回。
请注意,如果需要执行某种异步保存,检查父级不允许您“暂停”过渡。为此,您可以实现以下内容。唯一的缺点是,您失去了精美的iOS样式/动画后退按钮。在此处也请小心使用交互式滑动手势。使用以下方法处理这种情况。
var backButton : UIBarButtonItem!
override func viewDidLoad() {
super.viewDidLoad()
// Disable the swipe to make sure you get your chance to save
self.navigationController?.interactivePopGestureRecognizer.enabled = false
// Replace the default back button
self.navigationItem.setHidesBackButton(true, animated: false)
self.backButton = UIBarButtonItem(title: "Back", style: UIBarButtonItemStyle.Plain, target: self, action: "goBack")
self.navigationItem.leftBarButtonItem = backButton
}
// Then handle the button selection
func goBack() {
// Here we just remove the back button, you could also disabled it or better yet show an activityIndicator
self.navigationItem.leftBarButtonItem = nil
someData.saveInBackground { (success, error) -> Void in
if success {
self.navigationController?.popViewControllerAnimated(true)
// Don't forget to re-enable the interactive gesture
self.navigationController?.interactivePopGestureRecognizer.enabled = true
}
else {
self.navigationItem.leftBarButtonItem = self.backButton
// Handle the error
}
}
}
如果您没有遇到viewWillAppear
viewDidDisappear
问题,我们来看一个例子。假设您有三个视图控制器:
detailVC
当您从listVC
转到settingsVC
并返回到时,可以跟踪上的呼叫listVC
列表>详细信息(按下detailVC)Detail.viewDidAppear
<-出现
详细信息>设置(按下settingsVC)Detail.viewDidDisappear
<-消失
然后回头...
设置>详细信息(弹出设置 VC)<- Detail.viewDidAppear
出现
详细信息>列表(弹出详细信息 VC)Detail.viewDidDisappear
<-消失
请注意,viewDidDisappear
不仅在返回时,而且在前进时,都会多次调用它。对于可能需要的快速操作,但是对于更复杂的操作(例如要保存的网络呼叫),可能不需要。
didMoveToParantViewController:
当视图不再可见时,用户可以进行工作。交互式
_ = self.navigationController?.popViewController(animated: true)
,也会调用此方法,因此不只是在按“后退”按钮时调用它。我正在寻找仅在按Back后才能正常使用的电话。
第一种方法
- (void)didMoveToParentViewController:(UIViewController *)parent
{
if (![parent isEqual:self.parentViewController]) {
NSLog(@"Back pressed");
}
}
第二种方法
-(void) viewWillDisappear:(BOOL)animated {
if ([self.navigationController.viewControllers indexOfObject:self]==NSNotFound) {
// back button was pressed. We know this is true because self is no longer
// in the navigation stack.
}
[super viewWillDisappear:animated];
}
那些声称这行不通的人被误认为:
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
if self.isMovingFromParent {
print("we are being popped")
}
}
很好 那么,是什么导致了这个事实却没有呢?
问题似乎是由于不同方法的错误实现导致的,即willMove(toParent:)
忘记调用的实现super
。
如果您在willMove(toParent:)
未调用的情况下实施super
,那么self.isMovingFromParent
将会出现false
,并且使用viewWillDisappear
将会失败。它没有失败;你把它弄坏了。
注意:真正的问题通常是第二个视图控制器检测到弹出了第一个视图控制器。请在此处查看更一般的讨论:统一的UIViewController“成为最前端”检测?
编辑评论认为,这应该是,viewDidDisappear
而不是viewWillDisappear
。
true
,即使滑动没有完全弹出,也会从视图控制器的左边缘返回交互式滑动弹出手势。因此,与其在in进行检查willDisappear
,不如在didDisappear
工作中进行检查。
我已经为这个问题玩了两天。IMO最好的方法就是创建一个扩展类和一个协议,如下所示:
@protocol UINavigationControllerBackButtonDelegate <NSObject>
/**
* Indicates that the back button was pressed.
* If this message is implemented the pop logic must be manually handled.
*/
- (void)backButtonPressed;
@end
@interface UINavigationController(BackButtonHandler)
@end
@implementation UINavigationController(BackButtonHandler)
- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item
{
UIViewController *topViewController = self.topViewController;
BOOL wasBackButtonClicked = topViewController.navigationItem == item;
SEL backButtonPressedSel = @selector(backButtonPressed);
if (wasBackButtonClicked && [topViewController respondsToSelector:backButtonPressedSel]) {
[topViewController performSelector:backButtonPressedSel];
return NO;
}
else {
[self popViewControllerAnimated:YES];
return YES;
}
}
@end
之所以UINavigationController
可行,是因为navigationBar:shouldPopItem:
每次弹出视图控制器时都会收到一个调用。在那里,我们检测是否按下了back(任何其他按钮)。您唯一要做的就是在按下后退键的视图控制器中实现协议。
backButtonPressedSel
如果一切正常,请记住手动将视图控制器弹出。
如果您已经子类化UINavigationViewController
并实现了,navigationBar:shouldPopItem:
请不要担心,这不会对其造成干扰。
您可能还对禁用后退手势感兴趣。
if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) {
self.navigationController.interactivePopGestureRecognizer.enabled = NO;
}
这适用于我在iOS 9.3.x中使用Swift的情况:
override func didMoveToParentViewController(parent: UIViewController?) {
super.didMoveToParentViewController(parent)
if parent == self.navigationController?.parentViewController {
print("Back tapped")
}
}
与此处的其他解决方案不同,这似乎不会意外触发。
作为记录,我认为这更多的是他在寻找...
UIBarButtonItem *l_backButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemRewind target:self action:@selector(backToRootView:)];
self.navigationItem.leftBarButtonItem = l_backButton;
- (void) backToRootView:(id)sender {
// Perform some custom code
[self.navigationController popToRootViewControllerAnimated:YES];
}
如前所述purrrminator
,答案elitalon
不是完全正确的,因为your stuff
即使以编程方式弹出控制器也会执行该答案。
到目前为止,我发现的解决方案不是很好,但是对我有用。除了elitalon
说什么,我还检查是否以编程方式弹出:
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
if ((self.isMovingFromParentViewController || self.isBeingDismissed)
&& !self.isPoppingProgrammatically) {
// Do your stuff here
}
}
您必须将该属性添加到控制器中,并将其设置为YES,然后以编程方式弹出该属性:
self.isPoppingProgrammatically = YES;
[self.navigationController popViewControllerAnimated:YES];
谢谢你的帮助!
最好的方法是使用UINavigationController委托方法
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated
使用此方法,您可以知道哪个控制器正在显示UINavigationController。
if ([viewController isKindOfClass:[HomeController class]]) {
NSLog(@"Show home controller");
}
我通过在左侧的navigationBar中添加UIControl解决了此问题。
UIControl *leftBarItemControl = [[UIControl alloc] initWithFrame:CGRectMake(0, 0, 90, 44)];
[leftBarItemControl addTarget:self action:@selector(onLeftItemClick:) forControlEvents:UIControlEventTouchUpInside];
self.leftItemControl = leftBarItemControl;
[self.navigationController.navigationBar addSubview:leftBarItemControl];
[self.navigationController.navigationBar bringSubviewToFront:leftBarItemControl];
而且您需要记住在视图消失时将其删除:
- (void) viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
if (self.leftItemControl) {
[self.leftItemControl removeFromSuperview];
}
}
就这样!
您可以使用后退按钮回调,如下所示:
- (BOOL) navigationShouldPopOnBackButton
{
[self backAction];
return NO;
}
- (void) backAction {
// your code goes here
// show confirmation alert, for example
// ...
}
对于快速版本,您可以在全局范围内执行类似操作
extension UIViewController {
@objc func navigationShouldPopOnBackButton() -> Bool {
return true
}
}
extension UINavigationController: UINavigationBarDelegate {
public func navigationBar(_ navigationBar: UINavigationBar, shouldPop item: UINavigationItem) -> Bool {
return self.topViewController?.navigationShouldPopOnBackButton() ?? true
}
}
在下面的一个中,您放入了要控制后退按钮动作的viewcontroller:
override func navigationShouldPopOnBackButton() -> Bool {
self.backAction()//Your action you want to perform.
return true
}
navigationShouldPopOnBackButton
来自哪里?它不是公共API的一部分。
对于具有UINavigationController的Swift:
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
if self.navigationController?.topViewController != self {
print("back button tapped")
}
}
7ynk3r的答案确实接近我最终使用的答案,但需要进行一些调整:
- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item {
UIViewController *topViewController = self.topViewController;
BOOL wasBackButtonClicked = topViewController.navigationItem == item;
if (wasBackButtonClicked) {
if ([topViewController respondsToSelector:@selector(navBackButtonPressed)]) {
// if user did press back on the view controller where you handle the navBackButtonPressed
[topViewController performSelector:@selector(navBackButtonPressed)];
return NO;
} else {
// if user did press back but you are not on the view controller that can handle the navBackButtonPressed
[self popViewControllerAnimated:YES];
return YES;
}
} else {
// when you call popViewController programmatically you do not want to pop it twice
return YES;
}
}
您应该签出UINavigationBarDelegate协议。在这种情况下,您可能要使用navigationBar:shouldPopItem:方法。
最终找到的解决方案..我们一直在寻找的方法是“ willShowViewController”,这是UINavigationController的委托方法
//IMPORT UINavigationControllerDelegate !!
class PushedController: UIViewController, UINavigationControllerDelegate {
override func viewDidLoad() {
//set delegate to current class (self)
navigationController?.delegate = self
}
func navigationController(navigationController: UINavigationController, willShowViewController viewController: UIViewController, animated: Bool) {
//MyViewController shoud be the name of your parent Class
if var myViewController = viewController as? MyViewController {
//YOUR STUFF
}
}
}
MyViewController
到PushedController
。