viewWillDisappear:确定是弹出视图控制器还是显示子视图控制器


134

我正在努力找到解决这个问题的好方法。在视图控制器的-viewWillDisappear:方法中,我需要找到一种方法来确定是由于视图控制器被推到导航控制器的堆栈上,还是由于视图控制器因弹出而消失了。

目前,我正在设置诸如的标志,isShowingChildViewController但是它变得相当复杂。我认为我可以检测到它的唯一方法是该-dealloc方法。

Answers:


228

您可以使用以下内容。

- (void)viewWillDisappear:(BOOL)animated {
  [super viewWillDisappear:animated];
  NSArray *viewControllers = self.navigationController.viewControllers;
  if (viewControllers.count > 1 && [viewControllers objectAtIndex:viewControllers.count-2] == self) {
    // View is disappearing because a new view controller was pushed onto the stack
    NSLog(@"New view controller was pushed");
  } else if ([viewControllers indexOfObject:self] == NSNotFound) {
    // View is disappearing because it was popped from the stack
    NSLog(@"View controller was popped");
  }
}

当然,这是可能的,因为在调用viewWillDisappear时,UINavigationController的视图控制器堆栈(通过viewControllers属性公开)已经更新。


2
完善!我不知道为什么我没有想到这一点!我想我不认为在调用消失方法之前不会改变堆栈!谢谢:-)
Michael Waterfall

1
我一直在尝试执行相同的操作,但是在viewWillAppear其中,似乎无论是通过推动视图控制器来显示视图控制器还是在弹出视图控制器之上的东西,viewControllers数组在两种方式上都是相同的!有任何想法吗?
Michael瀑布

我还应该注意,视图控制器在应用程序的整个生命周期中都是持久的,因此我无法对其执行操作,viewDidLoad因为它仅被调用一次!嗯,棘手的一个!
Michael瀑布

4
@Sbrocket是否有您没有![viewControllers containsObject:self]代替的原因[viewControllers indexOfObject:self] == NSNotFound?风格选择?
zekel 2012年

24
自iOS 5起,此答案就已过时。-isMovingFromParentViewController下面提到的方法使您可以测试是否显式弹出了视图。
grahamparks 2013年

136

我认为最简单的方法是:

 - (void)viewWillDisappear:(BOOL)animated
{
    if ([self isMovingFromParentViewController])
    {
        NSLog(@"View controller was popped");
    }
    else
    {
        NSLog(@"New view controller was pushed");
    }
    [super viewWillDisappear:animated];
}

迅速:

override func viewWillDisappear(animated: Bool)
{
    if isMovingFromParent
    {
        print("View controller was popped")
    }
    else
    {
        print("New view controller was pushed")
    }
    super.viewWillDisappear(animated)
}

从iOS 5开始,这就是答案,也许还可以检查isBeingDismissed
d370urn3ur 2013年

4
对于iOS7,我必须再次检查[self.navigationController.viewControllers indexOfObject:self] == NSNotFound,因为后台应用程序也将通过此测试,但不会从导航堆栈中删除self。
Eric Chen

3
苹果提供了一种有据可查的方法来实现此目的-stackoverflow.com/a/33478133/385708
Shyam Bhat 2015年

使用viewWillDisappear的问题是,在视图已消失的情况下,有可能控制器从堆栈中弹出。例如,可以将另一个viewcontroller推入堆栈的顶部,然后调用popToRootViewControllerAnimated绕过中间的viewWillDisappear。
约翰·K

假设您的导航堆栈上有两个控制器(root vc和另一个推送的)。当第三个推入视图时,在第二个视图将消失的第二个视图上调用viewWillDisappear,对吗?因此,当您弹出到根视图控制器(弹出第三和第二个)时,viewWillDisappear在第三个(即最后一个vc)上被调用,因为它的视图位于顶部,并且此时将消失并且第二个视图已经消失。这就是为什么此方法称为viewWillDisappear而不是viewControllerWillBePopped的原因。
RTasche 2013年

61

从UIViewController.h中的Apple文档中:

“这四种方法可以在视图控制器的外观回调中使用,以确定它是否作为子视图控制器被呈现,取消或添加或删除。例如,视图控制器可以检查它是否由于取消而消失了,或者通过检查表达式([self isBeingDismissed] || [self isMovingFromParentViewController])来弹出自己的viewWillDisappear:方法。

- (BOOL)isBeingPresented NS_AVAILABLE_IOS(5_0);

- (BOOL)isBeingDismissed NS_AVAILABLE_IOS(5_0);

- (BOOL)isMovingToParentViewController NS_AVAILABLE_IOS(5_0);

- (BOOL)isMovingFromParentViewController NS_AVAILABLE_IOS(5_0);

因此,是的,唯一记录在案的方法是以下方法:

- (void)viewWillDisappear:(BOOL)animated
{
    [super viewWillDisappear:animated];
    if ([self isBeingDismissed] || [self isMovingFromParentViewController]) {
    }
}

Swift 3版本:

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    
    if self.isBeingDismissed || self.isMovingFromParentViewController { 
    }
}

18

如果你只是想知道你的观点是否得到弹出,我才发现,原来self.navigationControllernilviewDidDisappear,当它从控制器的堆栈中删除。这是一个简单的替代测试。

(这是我在尝试了各种其他扭曲之后发现的。我很惊讶没有导航控制器协议来注册要在弹出窗口中收到通知的视图控制器。您不能使用,UINavigationControllerDelegate因为这实际上可以完成实际的显示工作。)


16

斯威夫特4

override func viewWillDisappear(_ animated: Bool)
    {
        super.viewWillDisappear(animated)
        if self.isMovingFromParent
        {
            //View Controller Popped
        }
        else
        {
            //New view controller pushed
        }
    }

6

在Swift中:

 override func viewWillDisappear(animated: Bool) {
    if let navigationController = self.navigationController {
        if !contains(navigationController.viewControllers as! Array<UIViewController>, self) {
        }
    }

    super.viewWillDisappear(animated)

}

确保用作!而不是
dfmuir

2

我发现苹果对此的文档很难理解。此扩展程序有助于查看每个导航的状态。

extension UIViewController {
    public func printTransitionStates() {
        print("isBeingPresented=\(isBeingPresented)")
        print("isBeingDismissed=\(isBeingDismissed)")
        print("isMovingToParentViewController=\(isMovingToParentViewController)")
        print("isMovingFromParentViewController=\(isMovingFromParentViewController)")
    }
}

1

这个问题已经很老了,但是我是无意中看到的,所以我想发布最佳实践(afaik)

你可以做

if([self.navigationController.viewControllers indexOfObject:self]==NSNotFound)
 // view controller popped
}

1

这适用于iOS7,不知道是否适用于其他任何iOS7。据我所知,viewDidDisappear该视图中已经弹出。这意味着当您查询时,self.navigationController.viewControllers您会得到一个nil。因此,只需检查是否为零即可。

TL; DR

 - (void)viewDidDisappear:(BOOL)animated
 {
    [super viewDidDisappear:animated];
    if (self.navigationController.viewControllers == nil) {
        // It has been popped!
        NSLog(@"Popped and Gone");
    }
 }

1

在iOS 6+中,Segues是解决此问题的非常有效的方法。如果您已在Interface Builder中为特定序列指定了标识符,则可以在中进行检查prepareForSegue

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if ([segue.identifier isEqualToString:@"LoginSegue"]) {
       NSLog(@"Push");
       // Do something specific here, or set a BOOL indicating
       // a push has occurred that will be checked later
    }
}

1

感谢@Bryan Henry,仍然可以在Swift 5中工作

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        if let controllers = navigationController?.children{
            if controllers.count > 1, controllers[controllers.count - 2] == self{
                // View is disappearing because a new view controller was pushed onto the stack
                print("New view controller was pushed")
            }
            else if controllers.firstIndex(of: self) == nil{
                // View is disappearing because it was popped from the stack
                print("View controller was popped")
            }
        }

    }

-1

我假设您的意思是说您将视图推入堆栈时,通过推入新视图将视图从导航控制器的堆栈下移。我会建议使用的viewDidUnload方法将添加NSLog到写点东西语句到控制台上,所以你可以看到正在发生的事情,你可能希望将添加NSLogviewWillDissappeer


-1

这是完成与sbrocket的回答相同的事情的类别:

标头:

#import <UIKit/UIKit.h>

@interface UIViewController (isBeingPopped)

- (BOOL) isBeingPopped;

@end

资源:

#import "UIViewController+isBeingPopped.h"

@implementation UIViewController (isBeingPopped)

- (BOOL) isBeingPopped {
    NSArray *viewControllers = self.navigationController.viewControllers;
    if (viewControllers.count > 1 && [viewControllers objectAtIndex:viewControllers.count-2] == self) {
        return NO;
    } else if ([viewControllers indexOfObject:self] == NSNotFound) {
        return YES;
    }
    return NO;
}

@end
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.