【问题标题】:How to detect if view controller is being popped of from the navigation controller?如何检测是否从导航控制器弹出视图控制器?
【发布时间】:2014-03-21 20:02:58
【问题描述】:

当顶视图控制器从我的导航控制器中弹出时,我目前需要实现一些代码。有没有办法检测视图控制器何时从导航控制器堆栈中弹出?

我想尽可能避免使用viewWillDisappearviewDidDisappear,因为我在我的项目中使用splitview,并且在主视图中选择不同的行也会触发viewWillDisappear /viewDidDisappear 方法。

【问题讨论】:

  • 为什么不使用导航控制器的委托方法?
  • 您仍然可以使用 viewWillDissapear/viewDidDisappear 来查看 viewcontroller 是否真的被弹出。这可能会有所帮助:stackoverflow.com/questions/1816614/…

标签: ios objective-c cocoa-touch uiviewcontroller uinavigationcontroller


【解决方案1】:

您可以使用视图控制器的isMovingFromParentViewController 属性检测是否正在弹出视图,如下所示:

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

isMovingFromParentViewController

返回一个布尔值,表示视图控制器在 从父级中移除的过程。

【讨论】:

  • @Nishant 我知道当视图消失时会调用 isMovingFromParentViewController,但视图可能会因为 ViewController 被推送或弹出而消失。这个方法isMovingFromParentViewController 是在 viewController 被推送到导航堆栈时调用还是仅在它被弹出时调用(例如后退按钮)?
  • @bibscy isMovingFromParentViewController 仅在视图控制器弹出而不是推送时调用。
  • “popToRootViewController”情况如何处理?
【解决方案2】:

2015 年 4 月 30 日更新

根据 phatmann 的反馈(下面的第一条评论),我很好奇自从我一年多前回答这个问题以来是否发生了变化。我整理了一个简单的示例应用程序,并得到了一些有趣的结果。

选项 1,示例

https://github.com/greymouser/TestNVC

我没有能力轻松测试 8.x 之前的版本,所以我不确定从那以后是否发生了变化。但是,我最初描述的行为仍然会发生。 然而,由于将测试应用程序放在一起,我确实注意到了一个我以前没有注意到的奇怪之处。

如果我只依赖{will,did}MoveToParentViewController,我注意到在rootVC 上推送第一个非rootVC 时有一个虚假的didMoveToParentViewController: 调用,其父级为!= nil(暗示它已添加,未删除)。在我最初回答的时候我没有遇到这种情况,因为我的 NVC 上通常有“永久”rootVC,并且没有在那里实现回调。请参阅将日志记录设置为 LOG_WILL_DID_MTPVC 的示例应用程序(在 ViewController.m 中)。这是我所见内容的 -- 为空间编辑 -- 快照:

TestNVC[] -[vc(rootVC) willMoveToParentViewController [entering]
TestNVC[] -[vc(rootVC) didMoveToParentViewController [entering]
TestNVC[] -[vc(1) willMoveToParentViewController [entering]
TestNVC[] -[vc(rootVC) didMoveToParentViewController [entering]  # <-- this is odd
TestNVC[] -[vc(1) didMoveToParentViewController [entering]
...

我最初的回答建议单独使用{will,did}MoveToParentViewController,因为它是处理这种行为的“一站式商店”。然而,既然我已经看到了对 rootVC 的虚假调用,我建议混合使用 {will,did}MoveToParentViewController 以及标准的 UINavigationControllerDelegate 回调。对于示例应用程序中的此行为,请将日志记录设置为 LOG_WILL_DID_MTPVC_LEAVING_AND_NVC_WILL_DID_SHOW_VC。现在我们看到以下内容:

TestNVC[] -[nvcD willShowViewController]: rootVC
TestNVC[] -[nvcD didShowViewController]: rootVC
TestNVC[] -[nvcD willShowViewController]: 1
TestNVC[] -[nvcD didShowViewController]: 1
TestNVC[] -[nvcD willShowViewController]: 2
TestNVC[] -[nvcD didShowViewController]: 2
TestNVC[] -[vc(2) willMoveToParentViewController [leaving]
TestNVC[] -[nvcD willShowViewController]: 1
TestNVC[] -[vc(2) didMoveToParentViewController [leaving]
TestNVC[] -[nvcD didShowViewController]: 1
TestNVC[] -[vc(1) willMoveToParentViewController [leaving]
TestNVC[] -[nvcD willShowViewController]: rootVC
TestNVC[] -[vc(1) didMoveToParentViewController [leaving]
TestNVC[] -[nvcD didShowViewController]: rootVC

...现在这更有意义了。

选项 2

我没有探索的另一个选项是使用您的 NVC 子类,覆盖 - pushViewController:animated:- popViewControllerAnimated:,并将您想要的任何行为应用于被推送的 VC 或从 pop 返回的 VC。 (如果您尝试这样做,请务必记住在您的覆盖中调用 super。)

更新摘要

所以,感谢 phatmann 有机会重新解决这个问题。我认为我的回答现在更多是正确的。但是,我不太确定它是否“完全不真实”。 ;-)

原创

如果您描述的确切行为是您正在寻找的,则在您的子视图控制器上覆盖以下内容:

- (void)willMoveToParentViewController:(UIViewController *)parent;
- (void)didMoveToParentViewController:(UIViewController *)parent;

willMoveToParentViewController: 将在进入时使用 parent != nil 调用,在离开时使用 parent == nil 调用。 didMoveToParentViewController: 总是有父级!= nil。

有时,viewDidDisappear 可能有意义。但是,如果您真的要从父容器视图控制器中寻找 push 和 pop,那么上面的这些方法就是您想要的。

【讨论】:

  • 至少在 iOS 8 中,导航控制器中的子视图控制器不会调用 willMoveToParentViewController。并且didMoveToParentViewController 在子视图控制器最初放置在导航视图控制器中时被调用,除了在弹出时被调用。所以这些方法对于 OP 想要的东西是没有用的。别无选择,只能投反对票。
  • @phatmann 感谢您的反馈。我也使用示例应用程序和日志记录更新了上面的答案。您的评论帮助我确定了我在第一个答案中没有注意到的问题,但是,我认为您的担忧/观点也不是 100% 正确的。请参阅更新以获取完整说明。干杯!
  • 哇,很棒的研究,感谢您这样做。现在赞成 :-) 但是,Apple 使这些电话变得很奇怪,以至于我将继续避免它们。最好使用布尔值,虽然很粗略。
【解决方案3】:

如果在视图控制器被移除之前不需要知道,只需要知道它已经被弹出,也可以使用deinit

class ViewController: UIViewController {

    deinit {
        // View controller has been popped/dismissed and it's being released
    }
}

此方法可以很好地通知协调员或其他代表。

【讨论】:

  • 这也可以,但请注意,如果您订阅了 NSNotificationCenter 事件或视图控制器正在等待计时器等,则可能不会调用 deinit。最好使用中描述的其他方法其他安全的答案。
  • 你不知道系统什么时候会真正deinit VC。例如,在默认的 master -> detail 场景中,只有在系统尝试打开该类的新 VC 时,才会取消详细信息 VC。这意味着在 pop 之后,VC 仍然不会被取消。它只会在下一次推送时发生。
  • 我从未见过这种行为。你确定你没有保留视图控制器,@inokey?
  • @EnekoAlonso 你应该问这很有趣,因为这种方法实际上帮助我调试了这个。最初我在课堂上有一个参考问题,VC 从未发布。但我现在很确定它正在发布,但只是在它被重新创建之后。仍然可能存在某种弱引用,它使 VC 的持有时间超过了应有的时间。我并不是要说您的方法完全无效,但我猜它可能不适合某些设置。
【解决方案4】:

对于 Swift 用户(Swift 3 - 4.2):

我想检测视图控制器何时从堆栈中弹出,因此我无法使用 viewWillDisappearviewDidDisappear 回调,因为当视图控制器不再可见时将调用这些回调,而不是当它从堆栈中弹出时。

但您可以通过执行以下操作来使用导航控制器 Delegates UINavigationControllerDelegate

让你的控制器符合UINavigationControllerDelegate:

class ViewController : UIViewController {

      override func viewDidLoad() {
          super.viewDidLoad()
          self.navigationController?.delegate = self
      }

}




extension ViewController : UINavigationControllerDelegate {
     
      override func willMove(toParent parent: UIViewController?) {
     
     /*You can detect here when the viewcontroller is being popped*/
      
     }

}

希望对你有帮助,祝你好运

【讨论】:

  • 在实例化视图控制器时也会调用此方法。
【解决方案5】:

我对 iOS 13 的体验是 isMovingFromParent 的属性值并不总是一致的。当搜索控制器处于活动模式时(点击搜索文本字段),返回父视图将具有此属性的 false 值。

这是我确定视图是否来自父级的方法:

class MyBaseViewController: UIViewController {
    private var _isPushedToAnotherView = false
    var isPushedToAnotherView: Bool {
      return _isPushedToAnotherView
    }
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
      super.prepare(for: segue, sender: sender)
      ...
      _isPushedToAnotherView = true
    }
    override func viewWillAppear(_ animated: Bool) {
      super.viewWillAppear(animated)
      ...
      _isPushedToAnotherView = false
    }
    ...
}

class MyExtendedClass: MyBaseViewController {
  ...
  override func viewDidDisappear(_ animated: Bool) {
    super.viewDidDisappear(animated)
    ...
    if !isPushedToAnotherView {
        // clear resources hold by this class
    }
}

【讨论】:

    猜你喜欢
    • 2019-10-07
    • 1970-01-01
    • 2019-06-29
    • 2012-02-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多