Swift中,当dismiss视图控制器后,viewWillAppear方法没有被调用

42

我正在从名为HomeController的视图控制器中呈现一个视图控制器,如下所示:

let viewController = self.storyboard?.instantiateViewController(withIdentifier: "LoginController") as! LoginController

let navigationController: UINavigationController = UINavigationController(rootViewController: viewController)

present(navigationController, animated: true, completion: nil)
在展示的视图控制器中,LoginController 在某个时刻被关闭(dismissed):
self.dismiss(animated: true, completion: nil)

但是当它返回到HomeController时,它没有调用viewWillAppear方法。我真的需要在HomeController中检查条件,那么当LoginController解除视图时,我该如何调用viewWillAppear方法呢?


控制器上的演示类型是什么? - Sulthan
我应该在哪里设置或查找它的类型?这只是一个普通的调用。 - user979331
如果演示文稿没有全屏或动画是自定义的,则外观方法不会在底层控制器上调用。 - Sulthan
6个回答

45

您需要设置正确的presentationStyle。如果您希望您的 presentedController 全屏显示,并在之前调用 viewWillAppear 方法,则可以使用 ".fullScreen"。

let viewController = self.storyboard?.instantiateViewController(withIdentifier: "LoginController") as! LoginController

let navigationController: UINavigationController = UINavigationController(rootViewController: viewController)

navigationController.modalPresentationStyle = .fullScreen

present(navigationController, animated: true, completion: nil)

1
当我从LoginController返回时,我尝试了viewWillAppear但它没有被调用。 - user979331
1
@user979331 对不起,我的意思是 .fullScreen - Kevinosaurio
2
您可以在Storyboard中单击segue,在属性检查器下更改展示方式为全屏。对我来说,比编写代码更容易。 - K. Law
1
我知道这是一个老问题,但仍然保持新鲜。我遇到了一个问题,即并非所有的segue都是全屏模态呈现。这让我很疯狂,但它是有道理的。所有以模态方式呈现的视图控制器必须是全屏的,否则视图将根本不会被调用。即使你错过了其中一个,当最后一个视图返回到根视图时,也不会调用视图将出现。 - Julian Silvestri
我已经解决了。通过代码设置帮助我检查解决方案,您也可以在导航控制器>属性检查器中更改它。 - tontonCD
显示剩余3条评论

16
如果呈现的视图控制器是半屏幕,则必须在呈现的视图控制器中手动调用viewWillAppear函数,同时位于被呈现的视图控制器的viewWillDisappear函数中。 请将以下代码添加到您的被呈现的视图控制器。
override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    presentingViewController?.viewWillDisappear(true)
}    

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    presentingViewController?.viewWillAppear(true)
}
注意:你必须调用 'presentingViewController?.viewWillDisappear(true)' 以便让你的呈现视图控制器的 'viewWillAppear' 在每次执行时都生效。

3
这是正确的解决方案。 - Struki84
天啊,我一直将呈现的VC作为代理传递给了弹出的VC。然后使用代理在呈现的VC中调用所需的函数。真是糟糕。这就是解决方案。“presentingViewController”。 - Sean Mayes
解决方案无法正常工作。 - Mihir Oza
这个解决方案会在控制台中抛出错误,并且会破坏UINavigationControllers中的动画 - 在这里查看我的解决方案https://dev59.com/DlUK5IYBdhLWcg3w7DgH#74618304 - Niall Mccormack
1
现在viewDidAppear方法在其他视图上停止工作了。 - Elad

9

将演示样式更改为.fullScreen可以起作用,但会改变所呈现的视图控制器的外观。如果您想避免这种情况,可以在被呈现的视图控制器中覆盖viewWillDisappear方法,并在其中添加presentingViewController?.viewWillAppear(true)

例如:

 class ViewControllerA: UIViewController {
 
      override func viewDidLoad() {
          super.viewDidLoad()
          
          //put the presenting action wherever you want

          let vc = ViewControllerB()
          navigationController.present(vc, animated: true)
      }

      override func viewWillAppear(_ animated: Bool) {
           super.viewWillAppear(animated) 
           //refresh Whatever 
      }
 }

 class ViewControllerB: UIViewController {
 
      override func viewDidLoad() {
          super.viewDidLoad()
      }

      override func viewWillDisappear(_ animated: Bool) {
          super.viewWillDisappear(animated)

          //this will call the viewWillAppear method from ViewControllerA  

          presentingViewController?.viewWillAppear(true)
      }
 }

1
好的,但只能第一次运行时有效。 - Paul Bénéteau
值得注意的是,使用这种方法时,如果ViewControllerB呈现了ViewControllerC,则当ViewControllerC出现时,也会调用viewWillDisappear()方法,这可能不是期望的结果。 - Derek Lee
此解决方案在控制台中引发错误,并破坏了UINavigationControllers中的动画 - 请在此处查看我的解决方案 https://dev59.com/DlUK5IYBdhLWcg3w7DgH#74618304 - Niall Mccormack

4

这些解决方案都不太令人满意,实际上建议调用 presentingViewController?.viewWillAppear(true) 的解决方案会破坏导航控制器中的动画,并在控制台中引发 UIKit 的警告。

正确的方法是使用 UIKit 中内置的方法,正如警告所建议的那样!这些方法将在呈现类中调用所有相应的方法 viewWill/DidAppear

请注意,我有一个 if 语句检查 modelPresentationStyle 是否等于 pageSheet,因为最近的 iOS 发布版本将默认呈现样式设置为此值。我们不想干扰其他呈现样式,因为它们可能已经调用了 viewWill/DidAppear,而且不应该被调用两次。

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)

    // view lifecycle events are not passed down to the presentingViewController if in pageSheet style (and maybe others)
    if modalPresentationStyle == .pageSheet {
        presentingViewController?.beginAppearanceTransition(false, animated: animated)
    }
}

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)

    // view lifecycle events are not passed down to the presentingViewController if in pageSheet style (and maybe others)
    if modalPresentationStyle == .pageSheet {
        presentingViewController?.endAppearanceTransition()
    }
}

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)

    // view lifecycle events are not passed down to the presentingViewController if in pageSheet style (and maybe others)
    if modalPresentationStyle == .pageSheet {
        presentingViewController?.beginAppearanceTransition(true, animated: animated)
    }
}

override func viewDidDisappear(_ animated: Bool) {
    super.viewDidDisappear(animated)

    // view lifecycle events are not passed down to the presentingViewController if in pageSheet style (and maybe others)
    if modalPresentationStyle == .pageSheet {
        presentingViewController?.endAppearanceTransition()
    }
}

3
presentingViewController in viewDidDisappear always nil. you may add private weak var presentingViewControllerBeforeDisappear: UIViewController?, and set presentingViewControllerBeforeDisappear = presentingViewController before presentingViewController?.beginAppearanceTransition(true, animated: animated) in viewWillDisappear, and then set presentingViewControllerBeforeDisappear?.endAppearanceTransition() in viewDidDisappear - Leo
谢谢Leo的评论,它解决了viewDidDisappear情况下的问题。@Eric,你能更新答案并包含Leo的修复吗? - Iosif
如果您在导航控制器中呈现一个视图控制器,然后将一个新的视图控制器推入此呈现的导航堆栈中,那么 presentingViewController 将会收到 viewWillAppear 的调用,这是不正确的。 - undefined

2
在iOS 13中,如果您正在呈现一个视图控制器,并且当您返回时viewWillAppear不会被调用。 我已将其从“present”更改为“push view controller”,现在方法得到了调用。

当您从VC1导航到VC2时,您必须编写当前视图控制器。只需将其更改为推动即可。 - Sateesh Pasala
1
另一种方法是更改展示样式: vc.modalPresentationStyle = UIModalPresentationFullScreen; - Sateesh Pasala
我想我明白你的意思,但是没有推送方法,我需要使用除了UIViewController之外的其他协议吗? - Smit
3
我刚刚尝试了一下,但是设置.fullscreen没有起作用,viewwillapear也没有被调用。 - famfamfam
同样的问题在这里,需要一个解决方案。 - famfamfam
显示剩余5条评论

1

Swift 5。对我来说很好用。也许大家都忘了委托。

第一个控制器

let calendarVC = CalendarVC()
calendarVC.delegate = self
calendarVC.modalTransitionStyle = .coverVertical
present(calendarVC, animated: true, completion: nil)

当前控制器

添加代理……

override func viewWillDisappear(_ animated: Bool) {
  super.viewWillDisappear(animated)
        delegate?.closeState()
 }

在滑动和关闭按钮时工作。祝您有美好的一天)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接