为啥让 viewController 自行关闭是不好的做法?
Posted
技术标签:
【中文标题】为啥让 viewController 自行关闭是不好的做法?【英文标题】:Why is it bad practice to have a viewController dismiss itself?为什么让 viewController 自行关闭是不好的做法? 【发布时间】:2015-07-03 10:50:09 【问题描述】:我有两个视图控制器,MainVC
和 ModalVC
。
当用户点击MainVC
上的按钮时,会出现模态视图控制器。
然后用户可以点击另一个按钮将其关闭并返回到主按钮。
我已经尝试了这两种方法,它们都完成了同样的事情:它们关闭了模态视图控制器:
//method 1:
// File: ModalVC.swift
//
@IBAction func dismissTapped()
self.dismissViewControllerAnimated(false, completion: nil);
正如我所说的那样工作正常,但考虑另一种方法:使用委托让主控制器执行解雇:
// method 2: part A
// File: ModalVC.swift
//
protocol ModalVCDelegate
func modalVCDismissTapped();
...
...
...
var delegat:ModalVCDelegate? = nil;
...
...
@IBAction func dismissTapped()
delegate.modalVCDismissTapped();
在主视图控制器自定义类文件上:
// method 2: part B
// File: MainVC.swift
class MainVC : UIViewController, ModalVCDelegate
...
...
func modalVCDismissTapped()
self.dismissViewControllerAnimated(false, completion: nil);
既然这两种方法都需要,我应该担心任何可能的内存泄漏吗?
任何解释都会有所帮助
【问题讨论】:
【参考方案1】:使用委托是解除视图控制器的最佳且更灵活的方式。
它的目的是在将来或在您的代码中的其他地方,您可以重用此 VC,但由于某些原因,您可能不会将其呈现为模态,而是推入导航堆栈。因此,您的 ModalVC
不知道它是如何呈现的,但委托却知道。
在这种情况下,您的代码中可以有 2 个位置
您将其呈现为模态并委托调用
[self dismiss...]
您将其推入导航堆栈并委托调用
[self.navigationController popView...]
您将其添加为子 VC 并委托调用
[someParentVC removeChild..]
或任何其他适当的工作流程来删除它。
【讨论】:
很好的答案。一句话总结:你的“模态”视图控制器不知道客户将如何使用/呈现它,因此它也必须负责解除它。 或者简而言之:创建和拥有它的人也应该处理它的解雇。【参考方案2】:Modo Ltunzher 的回答很好。 我个人更喜欢将闭包传递给“孩子”,孩子在完成后会回调“父亲”并提供奖励,我也可以传回值/结果。
一个例子:我展示一个二维码,当二维码被识别时,它会关闭并回调:
extension UIViewController
func presentQRCode( pushed: Bool, callback: @escaping QRCodeCallBack)
let qrVC = ScanQRCodeController(nibName: "ScanQRCodeController", bundle: nil)
qrVC.callback = callback
if pushed
let nc = self.navigationController!
nc.pushViewController(qrVC, animated: true)
else
self.present(qrVC, animated: true, completion: nil)
func dismissQRCode(pushed: Bool)
if pushed
let nc = self.navigationController!
nc.popViewController(animated: true)
else
self.dismiss(animated: true, completion: nil)
在“父亲”中
@IBAction func doScanCodeAction(_ sender: UIBarButtonItem)
let pushed = true
self.presentQRCode(pushed: pushed, callback: (string: String?) in
if let qrCode = string
self.fillFieldsWith(qrCode: qrCode)
else
#if DEBUG
print("QR code error")
#endif
self.dismissQRCode(pushed: pushed)
)
【讨论】:
【参考方案3】:呈现的视图控制器不知道它正在被呈现,所以它不应该知道要关闭自己。
Apple 建议从呈现视图控制器 https://developer.apple.com/documentation/uikit/uiviewcontroller/1621505-dismiss 中关闭呈现的视图控制器
为避免在您的情况下发生泄漏,请始终将您的委托变量声明为 weak。 为此,您的协议应继承自 AnyObject。
protocol ModalVCDelegate: AnyObject
func modalVCDismissTapped()
weak var delegate: ModalVCDelegate?
另一种方法是在呈现的 VC 上创建一个闭包变量,并在呈现的 VC 上初始化它之后传递 dismiss 操作,然后在执行操作时调用闭包介绍的 VC。
演示 VC 设置
class PresentingViewController: UIViewController
@IBAction func buttonTapped(_ sender: Any)
let presentedVC = PresentedViewController()
presentedVC.modalPresentationStyle = .fullScreen
presentedVC.onDismissAction = [weak self] in
self?.dismiss(animated: true)
self.present(presentedVC, animated: true, completion: nil)
为关闭提供的 VC 设置
class PresentedViewController: UIViewController
var onDismissAction: (() -> Void)?
@IBAction func exitButtonTapped(_ sender: Any)
onDismissAction?()
【讨论】:
以上是关于为啥让 viewController 自行关闭是不好的做法?的主要内容,如果未能解决你的问题,请参考以下文章
为啥使用 SQLAlchemy?编码是不是非常方便? [关闭]
Storyboard Modal ViewController 不会关闭;已经尝试了一切
关闭模态呈现的 ViewController 总是让我回到根目录