【问题标题】: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);
    }
}

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

任何解释都会有所帮助

【问题讨论】:

    标签: ios swift uiviewcontroller


    【解决方案1】:

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

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

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

      [self.navigationController popView...]
      
    3. 您将其添加为子 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?()
        }
      }
      

      【讨论】:

        猜你喜欢
        • 2015-06-21
        • 2014-02-01
        • 2014-02-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-09-26
        • 2012-04-20
        • 2010-09-26
        相关资源
        最近更新 更多