为什么让 viewController 解雇自己是不好的做法?

Why is it bad practice to have a viewController dismiss itself?

我有两个视图控制器,MainVCModalVC

当用户点击 MainVC 上的按钮时,模态视图控制器出现。

然后用户可以点击另一个按钮将其关闭,然后 return 到主按钮。

我试过这两种方法,它们都完成了同样的事情:它们关闭了模态视图控制器:

//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();
}

并在主视图控制器上自定义 class 文件:

// method 2: part B
// File: MainVC.swift

class MainVC : UIViewController, ModalVCDelegate {
...
...
    func modalVCDismissTapped() {
        self.dismissViewControllerAnimated(false, completion: nil);
    }
}

既然这两种方法都可以,我是否应该担心任何可能的内存泄漏?

任何解释都会有所帮助

使用委托是关闭视图控制器的最佳且更灵活的方式。
它的目的是在将来或在您的代码中的其他地方您可以重用此 VC,但由于某些原因您可能不会以模态呈现它,而是将其推入导航堆栈。所以你的 ModalVC 不知道它是如何呈现的,但代表知道。
在这种情况下,您的代码中可以有 2 个位置

  1. 您将其呈现为模态并委托调用

    [self dismiss...]
    
  2. 您将其推入导航堆栈并委托调用

    [self.navigationController popView...]
    
  3. 您将其添加为子项 VC 并委托调用

    [someParentVC removeChild..] 
    

    或任何其他适当的工作流程来删除它。

Modo Ltunzher 的回答很好。 我个人更喜欢将闭包传递给 "child" child 完成后会回调 "father" 并提供奖励,我也可以传回值/结果。

举个例子:我展示了一个二维码,当识别到二维码时,它会关闭并回调:

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)
        }
    }
}

在"father"

   @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)
        }
        )
    }

呈现的视图控制器不知道它正在呈现,所以它不应该知道要关闭自己。

Apple 建议从呈现视图控制器中关闭呈现视图控制器https://developer.apple.com/documentation/uikit/uiviewcontroller/1621505-dismiss

为了避免泄漏,请始终将您的委托变量声明为 weak。 为此,您的协议应继承自 AnyObject.

protocol ModalVCDelegate: AnyObject {
   func modalVCDismissTapped()
}

weak var delegate: ModalVCDelegate?

另一种方法是在呈现的 VC 上创建一个闭包变量,并在初始化后传递 dismiss 操作呈现 VC,然后在对呈现的 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?()
  }
}