viewWillDisappear:判断视图控制器是被弹出还是显示子视图控制器

Posted

技术标签:

【中文标题】viewWillDisappear:判断视图控制器是被弹出还是显示子视图控制器【英文标题】:viewWillDisappear: Determine whether view controller is being popped or is showing a sub-view controller 【发布时间】:2010-12-21 10:58:59 【问题描述】:

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

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

【问题讨论】:

【参考方案1】:

我假设您的意思是当您说推入堆栈时,通过推入新视图将您的视图向下移动到导航控制器的堆栈中。我建议使用viewDidUnload 方法添加NSLog 语句来向控制台写入一些内容,以便您查看发生了什么,您可能需要将NSLog 添加到viewWillDissappeer

【讨论】:

【参考方案2】:

您可以使用以下内容。

- (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");
  

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

【讨论】:

完美!我不知道为什么我没有想到!我想在调用消失方法之前,我认为堆栈不会被更改!谢谢:-) 我一直在尝试执行相同的操作,但是在 viewWillAppear 中,似乎视图控制器是通过被推送还是在它上面的东西被弹出来显示的,viewControllers 数组两种方式都一样!有什么想法吗? 我还应该注意,视图控制器在应用程序的整个生命周期中都是持久的,所以我不能在 viewDidLoad 上执行我的操作,因为它只被调用一次!嗯,棘手的一个! @Sbrocket 你没有使用![viewControllers containsObject:self] 而不是[viewControllers indexOfObject:self] == NSNotFound 有什么原因吗?风格选择? 这个答案自 ios 5 以来已经过时。下面提到的 -isMovingFromParentViewController 方法允许您测试视图是否被显式弹出。【参考方案3】:

如果你只是想知道你的视图是否被弹出,我刚刚发现self.navigationControllerviewDidDisappear 中是nil,当它从控制器堆栈中删除时。所以这是一个简单的替代测试。

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

【讨论】:

【参考方案4】:

这是一个完成与 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

【讨论】:

【参考方案5】:

我认为最简单的方法是:

 - (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 对于 iOS7,我必须再次检查 [self.navigationController.viewControllers indexOfObject:self] == NSNotFound,因为后台应用程序也将通过此测试,但不会从导航堆栈中删除 self。 Apple 提供了一个记录的方法来做到这一点 - ***.com/a/33478133/385708 使用 viewWillDisappear 的问题是控制器可能从堆栈中弹出,而视图已经消失。例如,可以将另一个 viewcontroller 压入堆栈顶部,然后调用 popToRootViewControllerAnimated 绕过中间的 viewWillDisappear。 假设您的导航堆栈上有两个控制器(root vc 和另一个 push)。当第三个被推送时 viewWillDisappear 在第二个视图将要消失的时候被调用,对吧?因此,当您弹出到根视图控制器(弹出第三个和第二个)时, viewWillDisappear 在第三个即堆栈上的最后一个 vc 上被调用,因为它的视图在顶部并且此时将消失,而第二个视图已经消失了。这就是为什么这个方法被称为 viewWillDisappear 而不是 viewControllerWillBePopped。【参考方案6】:

这个问题相当老了,但我偶然看到它,所以我想发布最佳实践(afaik)

你可以这样做

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

【讨论】:

【参考方案7】:

这适用于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");
    
 

【讨论】:

【参考方案8】:

在 iOS 6+ 中,Segues 是处理这个问题的一种非常有效的方法。如果您在 Interface Builder 中为特定的 segue 提供了一个标识符,您可以在 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
    

【讨论】:

【参考方案9】:

在斯威夫特中:

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

    super.viewWillDisappear(animated)


【讨论】:

请务必用作!而不是作为【参考方案10】:

来自 Apple 在 UIViewController.h 中的文档:

"这四种方法可以用在视图控制器的外观中 回调以确定它是否被呈现、关闭或添加 或作为子视图控制器移除。例如,视图控制器 可以检查它是否因为它被解雇或弹出而消失 通过在其 viewWillDisappear: 方法中询问自己 表达式 ([self isBeingDismissed] || [self isMovingFromParentViewController])。”

- (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  
    

【讨论】:

【参考方案11】:

斯威夫特 4

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

【讨论】:

【参考方案12】:

我发现 Apple 在这方面的文档很难理解。此扩展有助于查看每个导航的状态。

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

【讨论】:

【参考方案13】:

感谢@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")
            
        

    

【讨论】:

以上是关于viewWillDisappear:判断视图控制器是被弹出还是显示子视图控制器的主要内容,如果未能解决你的问题,请参考以下文章

当标签栏项目选择视图控制器集时调用 viewWillDisappear

UINavigationController 问题。视图控制器的 viewWillDisappear/viewDidDisappear 在应用程序启动时不调用

viewWillAppear 和 viewWillDisappear 之间的排序问题

通过在父级上呈现新的视图控制器 viewWillDisappear 在我取消订阅事件时触发

动画 viewWillAppear 和 viewWillDisappear?

知道 -viewWillAppear 何时触发但 -viewWillDisappear 尚未触发