Swift 如何在目标视图加载前触发 "prepare for segue"?

Swift how to trigger "prepare for segue" before target view load?

我正在尝试从 firestore(google 数据库)加载数据并希望在 tableview 上显示。

所以在第一个VC中,通过prepare函数,从数据库中获取数据,并传输到第二个VC(tableview)。但是有一个问题。我了解到准备函数在 viewdidload 之前进行,在我的应用程序中,准备函数在第二次 VC 加载之后进行。

这是我的代码。

先VC

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        let docRef = db.collection("Posting").getDocuments(){(querySnapshot, err) in
        if let err = err{
            print("errror getting documents")
        }else{
            for document in querySnapshot!.documents{
                print("\(document.documentID) => \(document.data())")
                self.savePostings.append(document.data())
            }
            print("\n\n\(self.savePostings)")
        }
        let vc = segue.destination as! PostingListTableViewController
        vc.updatedPostings = self.savePostings
        vc.testPrint = "잉 기모찌"
        print("배열 전달 확인\n\(vc.updatedPostings)\n\n\(self.savePostings)")
    }
    
}

第二个VC(表格视图)

class PostingListTableViewController: UITableViewController {

//private var postings: Array<[String:Any]> = []
private var documents: [DocumentSnapshot] = []
var updatedPostings: Array<[String:Any]?> = []
var testPrint:String = ""

override func viewDidLoad() {
    super.viewDidLoad()
    print("view did load")
    // Uncomment the following line to preserve selection between presentations
    // self.clearsSelectionOnViewWillAppear = false

    // Uncomment the following line to display an Edit button in the navigation bar for this view controller.
    // self.navigationItem.rightBarButtonItem = self.editButtonItem
}

// MARK: - Table view data source



override func numberOfSections(in tableView: UITableView) -> Int {
    // #warning Incomplete implementation, return the number of sections
    return 1
}

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    // #warning Incomplete implementation, return the number of rows
    return updatedPostings.count
}


override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "myTableCell", for: indexPath)
    cell.textLabel?.text = updatedPostings[indexPath.row]!["text"] as! String

    // Configure the cell...

    return cell
}

}

正如@vadian 所说的那样,您的问题是您正在进行异步调用。

prepare(for segueviewDidLoad 之前被调用,但是您在之后的某个时间更新您的属性,当您的请求完成时,那是在 viewDidLoad.

之后

相反,我建议您使用以下内容:

  1. 删除你的 segue,add identifier 到目标视图控制器

  2. tableView:didSelectRowAtIndexPath: 运行 你的 getDocuments 里面(或者在 IBAction 里面,如果这是一个按钮 segue)

    2.1。你可以显示一些进度指示器,以便用户知道延迟的原因

  3. 完成后,使用 instantiateViewControllerWithIdentifier 从故事板创建视图控制器并手动呈现。在这种情况下,您无需等待 prepare(for segue 来设置您的属性。

如果您的 segue 从单元调用,您可以将视图控制器添加为委托,如下所示:

然后你需要让你的视图控制器符合UITableViewDelegate,并且当用户按下一个单元格时将调用didSelectRowAt方法。您可以从 indexPath.row

获取手机号码
extension PostingListTableViewController: UITableViewDelegate {
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        let docRef = db.collection("Posting").getDocuments(){(querySnapshot, err) in
            if let err = err{
                print("errror getting documents")
            }else{
                for document in querySnapshot!.documents{
                    print("\(document.documentID) => \(document.data())")
                    self.savePostings.append(document.data())
                }
                print("\n\n\(self.savePostings)")
            }
            // not sure if completion of getDocuments is called on main thread, if it does - you don't need this line
            DispatchQueue.main.async {
                let vc = storyboard!.instantiateViewController(identifier: "storyboard_identifier") as! PostingListTableViewController
                vc.updatedPostings = self.savePostings
                vc.testPrint = "잉 기모찌"
                present(vc, animated: true)
                print("배열 전달 확인\n\(vc.updatedPostings)\n\n\(self.savePostings)")
            }
        }
    }
}

如果您从普通按钮而不是单元格执行此转场,您可以使用@IBAction 执行相同操作:

@IBAction @objc func push() {
    let docRef = db.collection("Posting").getDocuments(){(querySnapshot, err) in
                if let err = err{
                    print("errror getting documents")
                }else{
                    for document in querySnapshot!.documents{
                        print("\(document.documentID) => \(document.data())")
                        self.savePostings.append(document.data())
                    }
                    print("\n\n\(self.savePostings)")
                }
                // not sure if completion of getDocuments is called on main thread, if it does - you don't need this line
                DispatchQueue.main.async {
                    let vc = storyboard!.instantiateViewController(identifier: "storyboard_identifier") as! PostingListTableViewController
                    vc.updatedPostings = self.savePostings
                    vc.testPrint = "잉 기모찌"
                    present(vc, animated: true)
                    print("배열 전달 확인\n\(vc.updatedPostings)\n\n\(self.savePostings)")
                }
            }
}

我首先要做的是 DELETE ❌ 您可能通过从 VC1 上的按钮拖放到 VC2 上创建的转场。将其替换为未附加到任何 UI 组件的常规 segue,以便您可以在代码中手动触发它(根据其标识符,不要忘记提供一个)。它将允许您首先执行一些异步代码,并且仅在获得回调时触发导航(如果数据获取成功)。

要创建这样的 segue:

  1. 在故事板中单击您的 VC1 => 现在应该会在 VC1 的正上方显示一个带有图标的小矩形
  2. Ctrl + 从黄色图标(可能在那个矩形的最左边)拖动到 VC2(在主视图中,只要它在 VC2 上,你把它放在哪里并不重要)
  3. 在点击新创建的 segue 后提供标识符,以便您可以从代码中触发它

那是为了 segue,但现在,什么时候触发它?

  1. 在按钮上创建一个 @IBAction,它应该触发获取 + 导航(或等效的,如 didSelectRowAtIndexPath)
  2. 这个 IBAction 应该调用另一个方法,如下所示:
private func fetchPostingAndNavigateIfSuccessful() {
   // Should probably first set the activity indicator to `.startAnimating()`
   let docRef = db.collection("Posting").getDocuments() { [weak self] querySnapshot, error in
       // Should probably set the activity indicator to `.stopAnimating()`
       guard error != nil,
             let documents = querySnapshot?.documents else {
             print("error getting documents") //probably a good place to display an alert
             return
       }
       let postingsData = documents.map { [=10=].data() }
       self?.performSegue(withIdentifier: "NavigateToPostingsSegue", sender: postingsData)
   }
}

你的 segue 准备看起来像这样:


override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    super.prepare(for: segue, sender: sender)
    if let postingListTableViewController = segue.destination as? PostingListTableViewController,
       let postingsData = sender as? [[String:Any]] {
        postingListTableViewController.updatedPostings = postingsData
    }
}