我有一个理论问题。现在我正在阅读 Apple 的 ViewController 指南。
他们写:
当需要关闭呈现的视图控制器时,首选的方法是让呈现的视图控制器关闭它。换句话说,只要有可能,呈现视图控制器的同一个视图控制器也应该负责解除它。尽管有几种技术可以通知呈现视图控制器其呈现的视图控制器应该被解除,但首选技术是委托。
但我无法解释,为什么我必须在呈现的 VC 中创建协议并添加委托变量,在呈现 VC 中创建委托方法以关闭呈现的 VC,而不是在呈现的视图控制器方法中简单调用
[self dismissViewControllerAnimated:NO completion:nil]
?
为什么首选更好?为什么苹果推荐它?
我认为 Apple 正在为一个可能很笨拙的 API 掩饰一下。
[self dismissViewControllerAnimated:NO completion:nil]
其实有点小题大做。尽管您可以合法地在呈现的视图控制器上调用它,但它所做的只是将消息转发到呈现的视图控制器。如果您想在关闭 VC 之外做任何事情,您将需要知道这一点,并且您需要以与委托方法相同的方式对待它 - 因为这几乎就是它的本质,一个烘焙的有点不灵活委托方法。
也许他们遇到了很多糟糕的代码,这些代码并不真正了解这些代码是如何组合在一起的,因此他们很谨慎。
但是,当然,如果您需要做的就是忽略这件事,请继续。
我自己的做法是一种妥协,至少它提醒了我是怎么回事:
[[self presentingViewController] dismissViewControllerAnimated:NO completion:nil]
[迅速]
self.presentingViewController?.dismiss(animated: false, completion:nil)
为 Swift 3 更新
我来到这里只是想解除当前(呈现的)视图控制器。我正在为任何出于相同目的来到这里的人做出这个答案。
导航控制器
如果您使用的是导航控制器,那么这很容易。
回到之前的视图控制器:
// Swift
self.navigationController?.popViewController(animated: true)
// Objective-C
[self.navigationController popViewControllerAnimated:YES];
回到根视图控制器:
// Swift
self.navigationController?.popToRootViewController(animated: true)
// Objective-C
[self.navigationController popToRootViewControllerAnimated:YES];
(感谢 this answer 的 Objective-C。)
模态视图控制器
当视图控制器以模态方式呈现时,您可以通过调用将其关闭(从第二个视图控制器)
// Swift
self.dismiss(animated: true, completion: nil)
// Objective-C
[self dismissViewControllerAnimated:YES completion:nil];
呈现视图控制器负责关闭它呈现的视图控制器。如果您在呈现的视图控制器本身上调用此方法,UIKit 会要求呈现的视图控制器处理解除。
所以它适用于呈现的视图控制器自己调用它。 Here 是一个完整的示例。
代表们
OP 的问题是关于使用代表解除视图的复杂性。
这个 Objective-C 的答案很深入。
这是一个 Swift 示例。
到目前为止,我不需要使用委托,因为我通常有一个导航控制器或模式视图控制器,但如果我将来确实需要使用 the delegate pattern,我将添加一个更新。
这是为了视图控制器的可重用性。
您的视图控制器不应该关心它是否被呈现为模式、推送到导航控制器或其他任何东西。如果您的视图控制器自行关闭,那么您假设它是以模态方式呈现的。您将无法将该视图控制器推送到导航控制器上。
通过实现一个协议,您可以让父视图控制器决定它应该如何呈现/推送和解除/弹出。
尝试这个:
[self dismissViewControllerAnimated:true completion:nil];
根据我的经验,当您需要从任何您想要的 ViewController 中将其关闭并为每个关闭它的视图控制器执行不同的任务时,它会派上用场。任何采用该协议的 viewController 都可以以自己的方式关闭视图。 (ipad vs iphone,或者从不同的视图dismiss时传递不同的数据,dismiss时调用不同的方法等。)
编辑:
因此,澄清一下,如果您只想关闭视图,我认为无需设置委托协议。如果您在从不同的呈现视图控制器中解除它后需要做不同的事情,这将是您使用委托的最佳方式。
Swift 3.0 //在swift中关闭视图控制器
self.navigationController?.popViewController(animated: true)
dismiss(animated: true, completion: nil)
引用视图 Controller Programming Guide,“视图控制器如何呈现其他视图控制器”。
呈现的视图控制器链中的每个视图控制器都有指向链中围绕它的其他对象的指针。换句话说,呈现另一个视图控制器的呈现视图控制器在其呈现视图控制器和呈现视图控制器属性中都具有有效对象。您可以根据需要使用这些关系来跟踪视图控制器链。例如,如果用户取消当前操作,您可以通过关闭第一个呈现的视图控制器来移除链中的所有对象。关闭视图控制器不仅会关闭该视图控制器,还会关闭它呈现的任何视图控制器。
所以一方面它实现了一个很好的平衡设计,良好的解耦等等......但另一方面它非常实用,因为你可以快速回到导航的某个点。
虽然,我个人更愿意使用展开转场而不是尝试向后遍历呈现的视图控制器树,这就是苹果在本章中引用的内容。
有一点是,这是一种很好的编码方法。它满足许多OOP
原则,例如 SRP、关注点分离等。
因此,呈现视图的视图控制器应该是关闭它的控制器。
例如,出租房屋的房地产公司应该有权收回房屋。
除了 Michael Enriquez 的回答之外,我还能想到另一个原因,为什么这可能是保护自己免受未确定状态的好方法:
假设 ViewControllerA 以模态方式呈现 ViewControllerB。但是,由于您可能没有为 ViewControllerA 编写代码,因此您不知道 ViewControllerA 的生命周期。在呈现您的视图控制器 ViewControllerB 后,它可能会关闭 5 秒(例如)。
在这种情况下,如果您只是使用 ViewControllerB 中的 dismissViewController
来关闭自己,那么您最终会处于未定义的状态——从您的角度来看,可能不是崩溃或黑屏,而是未定义的状态。
相反,如果您使用的是委托模式,您会知道 ViewControllerB 的状态,并且可以针对我描述的情况进行编程。
迅速
let rootViewController:UIViewController = (UIApplication.shared.keyWindow?.rootViewController)!
if (rootViewController.presentedViewController != nil) {
rootViewController.dismiss(animated: true, completion: {
//completion block.
})
}
我喜欢这个:
(viewController.navigationController?.presentingViewController
?? viewController.presentingViewController
?? viewController).dismiss(animated: true)
如果您使用模态,请使用视图关闭。
[self dismissViewControllerAnimated:NO completion:nil];
这是胡说八道。委托在需要时很好,但如果它使代码更复杂——确实如此——那么就需要有它的理由。
我相信苹果有它的理由。但是,除非有真正的理由不这样做,而且到目前为止,这里还没有人提出我能看到的,否则简单地让所介绍的 VC 进行解雇会更清晰、更简洁。
协议在需要时非常出色,但面向对象的设计绝不是让模块不必要地相互通信。
Tom Love(Objective C 的共同开发者)曾经评论说,Objective C 是“优雅的”、“小巧的”、“清晰的”和“定义明确的”(与 C++ 相比)。他说得轻松。委托是一个有用的功能,似乎“仅仅因为”而被过度使用,虽然我喜欢使用该语言工作,但我害怕感觉被迫使用不必要的语法来使事情变得比它们必须的更复杂的想法。
只关注问题的标题,这是正确的答案。
presentedViewController?.dismiss(animated: true)
presentingViewController
几乎没有用,因为如果self
嵌入其中,它将引用UINavigationController
。在这种情况下,您将根本无法获得presentingViewController
。然而,[self dismissViewControllerAnimated:completion]
在这种情况下仍然有效。我的建议是继续使用它,直到 Apple 修复它。