为什么让 viewController 解雇自己是不好的做法?
Why is it bad practice to have a viewController dismiss itself?
我有两个视图控制器,MainVC
和 ModalVC
。
当用户点击 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 个位置
您将其呈现为模态并委托调用
[self dismiss...]
您将其推入导航堆栈并委托调用
[self.navigationController popView...]
您将其添加为子项 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?()
}
}
我有两个视图控制器,MainVC
和 ModalVC
。
当用户点击 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 个位置
您将其呈现为模态并委托调用
[self dismiss...]
您将其推入导航堆栈并委托调用
[self.navigationController popView...]
您将其添加为子项 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?()
}
}