swift如何控制ViewControllers之间的执行顺序?

How to control the order of execution between ViewControllers in swift?

我想实现的需求非常简单和常见,但我无法实现。我的示例代码尽可能简短,并应实现以下目标:

  1. 启动主视图。
  2. 在主视图上注册一个触摸事件。这将启动子视图。 (此调用定义了一个函数,该函数应在完成后开始。)
  3. 子视图打开。此子视图有一个 return 按钮。
  4. 按下此按钮后,控件将返回到主视图。
  5. 在主进程中,完成函数应该启动。
  6. 在主进程中,执行子视图调用(2)之后的语句。

但是我得到的执行顺序是这样的:

步骤 3 和步骤 4 是通过 self.present... 和 self.dismiss... 实现的,因为我需要在我的项目中以编程方式实现这些步骤(比这个演示版本更复杂).这些方法确实有效。我的问题只是执行顺序。

如果这个问题的答案是 await 和 async: 不幸的是我不能使用它,因为我被限制在 Xcode 10 和 Swift 4。但我希望上面的问题一定是也可以用旧方法解决。由于我是这个开发平台的新手,所以很可能我对这个过程缺乏基本的了解。

现在是我的 Swift 代码。 主视图控制器代码:

import UIKit

class ViewController: UIViewController {

override func viewDidLoad() {
    super.viewDidLoad()
    print("Intended step 1 (at main view start)")
}

@IBAction func gotoSubView(_ sender: UITapGestureRecognizer) {
    
    print("Intended step 2 (after tap in main)")
    let nextViewController = 
    self.storyboard?.instantiateViewController(withIdentifier: 
    "SubViewController") as! SubViewController
    
    self.present(nextViewController, animated:true, 
    completion:IntendedAfterReturn)
    print("Intended step 6 (after statement calling subview)")
  }

  func IntendedAfterReturn() {
    print("Intended step 5 (in function of completion. parameter)")
  }
}

子视图控制器代码:

import UIKit

class SubViewController: UIViewController {

@IBOutlet weak var btnReturn: UIButton!

override func viewDidLoad() {
    super.viewDidLoad()
    print("Intended step 3 (at sub view start)")
 }

@IBAction func backToMain(_ sender: UIButton) {
    print("Intended step 4 (after return Button is pressed)")
    self.dismiss(animated: true, completion: nil)
 }  
}

函数 present(_:animated:completion:) 不允许您提供在用户关闭模式时运行的完成处理程序。当显示模态的动画结束时(当呈现的模态完全显示在屏幕上时)它调用完成处理程序。

您需要向第一个视图控制器的 viewDidAppear 方法添加一个处理程序(加上区分第一次调用和用户从子视图控制器返回的逻辑)

或者您需要向您的子视图控制器添加一个委托 属性 以告知调用者用户已关闭子视图控制器。 (我推荐这种方法。使用 viewDidAppear 很脆弱。)

Duncan 关于添加代表的建议 属性 是正确的答案,它让我找到了解决方案。我想在这里分享上面问题的代码,并通过委托机制进行了增强,以防它可以帮助遇到类似问题的人。为了理解 delegate/protocol 机制,我找到了 Sean Allen 的一个很棒的解释视频:https://www.youtube.com/watch?v=DBWu6TnhLeY

现在又是代码。主视图控制器代码:

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
    print("Intended step 1 (at main view start)")
    }

    @IBAction func gotoSubView(_ sender: UITapGestureRecognizer) 
    {
        
        print("Intended step 2 (after tap in main)")
        let nextViewController = self.storyboard?.instantiateViewController(withIdentifier: "SubViewController") as! SubViewController
        nextViewController.selectionDelegate = self
        self.present(nextViewController, animated:true, completion:nil)
    }
}

extension ViewController: SubViewSelectionDelegate {
    func IntendedAfterReturn(text: String) {
        print("Intended step 5 (Text from subview is: \(text))")
    }
    
    func IntendedAfterReturnAlso(text: String) {
        print("Intended step 6  (Text from subview is: \(text))")
    }
}

子视图控制器代码:

    import UIKit

protocol SubViewSelectionDelegate {
    // Since we have only a primitive example here with 1 button, the function parameter would even not be necessary. But we include a parameter for demonstration.
    func IntendedAfterReturn(text: String)
    func IntendedAfterReturnAlso(text: String)
}

class SubViewController: UIViewController {
 
    var selectionDelegate: SubViewSelectionDelegate!
    
    
    @IBOutlet weak var btnReturn: UIButton!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        print("Intended step 3 (at sub view start)")
    }
  
    @IBAction func backToMain(_ sender: UIButton) {
        print("Intended step 4 (after return Button is pressed)")
        selectionDelegate.IntendedAfterReturn(text: "For first action after return button tap")
        selectionDelegate.IntendedAfterReturnAlso(text: "For second action after return button tap")
        self.dismiss(animated: true, completion: nil)
    }
    
}

现在,当我运行这个程序时,输出如下:

  • 预期的第 1 步(在主视图开始时)
  • 预期的第 2 步(在 main 中点击后)
  • 预期的第 3 步(在子视图开始时)
  • 预期步骤 4(按下 return 按钮后)
  • 预期的第 5 步(来自子视图的文本是:点击 return 按钮后的第一个动作)
  • 预期的第 6 步(来自子视图的文本是:对于之后的第二个操作 return 点击按钮)