如何在Segue传递数据后重新加载tableView数据

时间:2016-06-23 20:04:02

标签: ios swift uitableview firebase

我有两个表视图。用户点击的一个和显示数据的一个。当用户单击第一个表视图中的单元格时,将对我的firebase数据库进行查询,并将查询存储在Array中。然后我通过segue传递数据。我使用了属性观察器,所以我知道变量正在设置中。通过使用断点,我能够确定我的变量在cellForRowAtIndexPath方法之前获得其值。我需要帮助在表格视图中显示数据。我不知道在哪里重新加载数据以使表格视图更新我的数据。我正在使用Swift。

编辑2:我已经解决了我的问题。我将发布我的第一个和第二个表视图,以便您可以看到我的解决方案。

FirstTableView

writeToParcel(...)

}

import UIKit
import Firebase
import FirebaseDatabase

class GenreTableViewController: UITableViewController {

let dataBase = FIRDatabase.database()

var genreArray = ["Drama","Classic,Comic/Graphic novel","Crime/Detective","Fable,Fairy tale","Fantasy","Fiction narrative", "Fiction in verse","Folklore","Historical fiction","Horror","Humour","Legend","Magical realism","Metafiction","Mystery","Mythology","Mythopoeia","Realistic fiction","Science fiction","Short story","Suspense/Thriller","Tall tale","Western,Biography","Autobiography","Essay","Narrative nonfiction/Personal narrative","Memoir","Speech","Textbook","Reference book","Self-help book","Journalism", "Religon"]

var ResultArray: [NSObject] = []
var infoArray:[AnyObject] = [] 

override func viewDidLoad() {
    super.viewDidLoad()
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
}

// MARK: - Table view data source

override func numberOfSectionsInTableView(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 genreArray.count
}

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath)

    // Configure the cell...

    cell.textLabel?.text = genreArray[indexPath.row]

    return cell
}

override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {

}

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {

    let DestViewController: ResultTableViewController = segue.destinationViewController as! ResultTableViewController

    if segue.identifier == "letsGo" {
        if let indexPath = self.tableView.indexPathForSelectedRow {
            let tappedItem = self.genreArray[indexPath.row]
            DestViewController.someString = tappedItem 
        }  
    }
}

}

为了更好的上下文和疑难解答,这里是我当前的tableView代码,它应该显示数据:

import UIKit
import Firebase
import FirebaseDatabase

class ResultTableViewController: UITableViewController {


let dataBase = FIRDatabase.database()
var SecondResultArray: [FIRDataSnapshot]! = []
var someString: String?{
    didSet {
      print("I AM A LARGE TEXT")
      print(someString)
    }
}

override func viewDidLoad() {

    let bookRef = dataBase.reference().child("books")

    bookRef.queryOrderedByChild("Genre")
        .queryEqualToValue(someString)
        .observeSingleEventOfType(.Value, withBlock:{ snapshot in
            for child in snapshot.children {

                self.SecondResultArray.append(child as! FIRDataSnapshot)
                //print(self.ResultArray)
            }

            dispatch_async(dispatch_get_main_queue()) {
                self.tableView.reloadData()
            }

        })

    super.viewDidLoad()

    // 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()
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}

// MARK: - Table view data source

override func numberOfSectionsInTableView(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 SecondResultArray.count
}

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCellWithIdentifier("cell2", forIndexPath: indexPath)

    // Configure the cell...

    let bookSnapShot: FIRDataSnapshot! = self.SecondResultArray[indexPath.row]

    let book = bookSnapShot.value as! Dictionary<String, String>

    let Author = book["Author"] as String!
    let Comment = book["Comment"] as String!
    let Genre = book["Genre"] as String!
    let User = book["User"] as String!
    let title = book["title"] as String!

    cell.textLabel?.numberOfLines = 0
    cell.textLabel?.lineBreakMode = NSLineBreakMode.ByWordWrapping

    cell.textLabel?.text = "Author: " + Author + "\n" + "Comment: " + Comment + "\n" + "Genre: " + Genre + "\n" + "User: " + User + "\n" +  "Title: " + title

    let photoUrl = book["bookPhoto"], url = NSURL(string:photoUrl!), data = NSData(contentsOfURL: url!)
        cell.imageView?.image = UIImage(data: data!)

    return cell
}

编辑:

这是我的第一个表视图控制器。我已经尝试使用完成处理程序,但我无法正确调用它,我的查询发生在didSelectRowAtIndexPath方法的事实我受到限制。请帮忙。

    import UIKit

    class ResultTableViewController: UITableViewController {

        var SecondResultArray: Array<NSObject> = []{
            willSet(newVal){ 
                print("The old value was \(SecondResultArray) and the new value is \(newVal)")
            }
            didSet(oldVal){
               print("The old value was \(oldVal) and the new value is \(SecondResultArray)")
               self.tableView.reloadData()        
            }
        }
        override func viewDidLoad() {
            print ("I have this many elements\(SecondResultArray.count)")
            super.viewDidLoad()
        }
        // MARK: - Table view data source
        override func numberOfSectionsInTableView(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 SecondResultArray.count
        }

        override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
            let cell = tableView.dequeueReusableCellWithIdentifier("cell2", forIndexPath: indexPath)
            cell.textLabel?.text = SecondResultArray[indexPath.row] as? String
            return cell
        }
    }

6 个答案:

答案 0 :(得分:2)

您可以在第一个prepareForSegue的{​​{1}}方法中将数据注入目标viewController,然后在UIViewController中重新加载UITableView。如果要异步获取数据,请使用completionHandler并在completionHandler中重新加载它。这是一个例子。

viewDidAppear

答案 1 :(得分:0)

尝试更新您的闭包以包含此内容:

dispatch_async(dispatch_get_main_queue()) {
    self.tableView.reloadData()
}

答案 2 :(得分:0)

编辑:

在第二次阅读时, 已经在使用完成处理程序,但我认为您没有看到它。让我纠正你的代码:

override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {


    let bookRef = self.dataBase.reference().child("books")
    let GenreSelector = self.genreArray[indexPath.row]
    bookRef.queryOrderedByChild("Genre")
        .queryEqualToValue(GenreSelector)
        .observeSingleEventOfType(.Value, withBlock:{ snapshot in
            // This here is your completion handler code!
            // I assume it is called asynchronously once your DB is done
            for child in snapshot.children {
                print("Loading group \((child.key!))")

                self.ResultArray.append(child as! NSObject)
            }
            print(self.ResultArray)

            self.performSegueWithIdentifier("letsGo", sender: self)
            // self.tableView.reloadData() // is this really needed
        })
    }
}

您定义了一个闭包,但根本没有调用它。无论如何,我都没有看到这样的理由,假设一旦数据库为您提供结果,就会调用该块。我错过了什么吗?

这已经是一个好的开始,但我认为你在这方面并没有完全掌握如何使用完成处理程序,但当然我可能错了。

我建立在user3861282的答案之上,并在my github创建了一个小型演示项目。

简而言之:您可以使用第一个表视图控制器的prepareForSegue:方法进行所有表间通信。在那里配置第二个表视图控制器(通过其变量)。那里没有任何关闭/完成处理程序。

然后在第二个视图控制器的viewWillAppear:方法中,开始加载(如果需要,还包括动画)。我建议像NSURLSession那样已经定义了一个完成处理程序。因为您使用来自远程的数据,停止任何加载动画并且您很好。

如果必须在第一个表视图控制器中定义完成处理程序 ,您甚至可以在第二个表视图控制器中将其设置为var。那样你就可以移交&#34;关闭,即&#34;代码&#34;。

或者,您可以在第一个表视图控制器中启动动画和远程请求,然后在完成后performSegueWithIdentifier启动动画和远程请求。在您的问题中,您写道,如果我理解正确,您想在第二个表视图控制器中加载。

上面的代码正确地定义了一个闭包,它需要一个完成处理程序(它也是一个闭包,所以你想要的那种双打),但你实际上从来没有在某个地方调用它。你也没有在闭包中调用完成处理程序。请参阅我的演示,了解它的工作原理。

我写的项目说明了一种方法(减去动画,没有足够的时间)。它还展示了如何定义自己的函数,期望完成处理程序,但正如我所说,框架中的标准远程连接提供了一个。

答案 3 :(得分:0)

这个怎么样:

假设您有一个从Firebase填充并存储在第一个tableViewController中的数组(myArray)。第二个tableViewController和一个连接它们的segue。

我们希望能够点击第一个tableviewController中的项目,让应用程序从Firebase(一个&#39;数据&#39;节点)中检索项目的详细数据,并在第二个tableViewController中显示详细数据

Firebase结构

some_node
   child_node_0
     data: some detailed data about child_node_0
   child_node_1
     data: some detailed data about child_node_1

在第二个tableViewContoller中:

var passedObject: AnyObject? {
    didSet {
      self.configView() // Update the view.
     }
}

点击第一个tableView中的项目调用以下函数

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
   if segue.identifier == "showListInSecondTable" {
          if let indexPath = self.tableView.indexPathForSelectedRow {
              let tappedItem = myArray[indexPath.row] as! String
              let keyOfTappedItem = tappedItem.firebaseKey //child_node_0 for example
              doFirebase(keyOfTappedItem)
            }
        }
    }

然后prepareForSegue调用以下内容从firebase加载数据,当快照在块中返回时,它会填充第二个tableView中的passedObject属性

func doFirebase(firebaseKey: String) {

  ref = myRootRef.childByAppendingPath("\(firebaseKey)/data")
  //if we want the detailed data for child_node_0 this would resolve
  //  to    rootRef/child_node_0/data
  ref.observeSingleEventOfType(.Value, { snapshot in
     let detailObjectToPass = snapshot.Value["data"] as! NSArray or string etc

     let controller = (segue.destinationViewController as! UINavigationController).myViewController as! SecondViewController
     controller.passedObject = detailObjectToPass
}

当然在secondController中,设置passedArray调用didSet并设置视图,并告诉tableView重新加载自己,显示传递的数组。

func configView() {
   //set up the view and buttons
   self.reloadData()
}

我做得非常快,所以忽略了错别字。模式是正确的,并满足问题。 (并且无需观察者启动!)

P.S。这是过度编码的方式,但我想演示流程并利用对firebase的异步调用,以便在数据在块中有效时加载第二个tableView。

答案 4 :(得分:0)

根据添加到帖子中的其他代码,问题是控制器变量超出范围。

所以这就是问题

class MyClass {

  func setUpVars {
    let x = 1
  }

  func doStuff {
    print(x)
  }
}

创建一个类并尝试打印x

的值
let aClass = MyClass()
aClass.setUpVars
aClass.doStuff

一旦setUpVars结束,这将不会打印任何内容(概念上),'x'变量超出范围。

,而

class MyClass {

  var x: Int

  func setUpVars {
    x = 1
  }

  func doStuff {
    print(x)
  }
}

将打印x,1的值。

所以真正的解决方案是你的viewControllers需要在你的课程(或应用程序)期间“保持活力”。

这是模式。在MasterViewController中

import UIKit

    class MasterViewController: UITableViewController {

        var detailViewController: DetailViewController? = nil

然后在你的MasterViewController viewDidLoad(或任何地方),创建detailViewController

 override func viewDidLoad() {
    super.viewDidLoad()
    let controllers = split.viewControllers //this is from a splitViewController
    self.detailViewController = 
        controllers[controllers.count-1].topViewController as? DetailViewController
 }

从那里你得到它...使用prepareForSegue'将'数据'发送'到detailViewController

只是希望将此发布以供将来参考。

答案 5 :(得分:-1)

您可以使用[tableView reloadData];重新加载TableView。