为啥让 viewController 自行关闭是不好的做法?

Posted

技术标签:

【中文标题】为啥让 viewController 自行关闭是不好的做法?【英文标题】:Why is it bad practice to have a viewController dismiss itself?为什么让 viewController 自行关闭是不好的做法? 【发布时间】:2015-07-03 10:50:09 【问题描述】:

我有两个视图控制器,MainVCModalVC

当用户点击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 自行关闭是不好的做法?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我的 Spring Boot 应用程序会自行关闭

为啥使用 SQLAlchemy?编码是不是非常方便? [关闭]

Storyboard Modal ViewController 不会关闭;已经尝试了一切

关闭模态呈现的 ViewController 总是让我回到根目录

iOS:如何让 ViewController A 模态呈现 B,然后让 B 直接关闭/转换到模态呈现的 C?

2 ViewControllers 以模态方式呈现 UITabBarController