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 segue
在 viewDidLoad
之前被调用,但是您在之后的某个时间更新您的属性,当您的请求完成时,那是在 viewDidLoad
.
之后
相反,我建议您使用以下内容:
删除你的 segue,add identifier 到目标视图控制器
在 tableView:didSelectRowAtIndexPath:
运行 你的 getDocuments
里面(或者在 IBAction
里面,如果这是一个按钮 segue)
2.1。你可以显示一些进度指示器,以便用户知道延迟的原因
完成后,使用 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:
- 在故事板中单击您的 VC1 => 现在应该会在 VC1 的正上方显示一个带有图标的小矩形
- Ctrl + 从黄色图标(可能在那个矩形的最左边)拖动到 VC2(在主视图中,只要它在 VC2 上,你把它放在哪里并不重要)
- 在点击新创建的 segue 后提供标识符,以便您可以从代码中触发它
那是为了 segue,但现在,什么时候触发它?
- 在按钮上创建一个 @IBAction,它应该触发获取 + 导航(或等效的,如 didSelectRowAtIndexPath)
- 这个 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
}
}
我正在尝试从 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 segue
在 viewDidLoad
之前被调用,但是您在之后的某个时间更新您的属性,当您的请求完成时,那是在 viewDidLoad
.
相反,我建议您使用以下内容:
删除你的 segue,add identifier 到目标视图控制器
在
tableView:didSelectRowAtIndexPath:
运行 你的getDocuments
里面(或者在IBAction
里面,如果这是一个按钮 segue)2.1。你可以显示一些进度指示器,以便用户知道延迟的原因
完成后,使用 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:
- 在故事板中单击您的 VC1 => 现在应该会在 VC1 的正上方显示一个带有图标的小矩形
- Ctrl + 从黄色图标(可能在那个矩形的最左边)拖动到 VC2(在主视图中,只要它在 VC2 上,你把它放在哪里并不重要)
- 在点击新创建的 segue 后提供标识符,以便您可以从代码中触发它
那是为了 segue,但现在,什么时候触发它?
- 在按钮上创建一个 @IBAction,它应该触发获取 + 导航(或等效的,如 didSelectRowAtIndexPath)
- 这个 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
}
}