8

I'm trying to reload my table view after updating data in Swift but it doesn't seems to work. When I change tab and go back the table view is reloaded but not automatically. Here is my code:

override func viewDidLoad() {
    super.viewDidLoad()

    // some code

    refresh(self)
}

func refresh(sender: AnyObject) {
    // Reload the data

    self.tableView.reloadData()
}

I don't understand why it works in Objective-C but not in Swift... I also tried to put :

dispatch_async(dispatch_get_main_queue(), { () -> Void in
    self.tableView.reloadData()
})

because I saw this in other post but it doesn't work either.

Thanks for helping

EDIT: My whole View Controller

class HighwaysViewController: UITableViewController {

    var highways: [Highway]!

    override func viewDidLoad() {
        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()

        highways = [Highway]()

        // On ajoute le "Pull to refresh"
        refreshControl = UIRefreshControl()
        refreshControl!.addTarget(self, action: Selector("refresh:"), forControlEvents: UIControlEvents.ValueChanged)
        tableView.addSubview(refreshControl!)

        refresh(self)
    }

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

    func refresh(sender: AnyObject) {
        // Afficher l'icône de chargement dans la barre de status
        UIApplication.sharedApplication().networkActivityIndicatorVisible = true

        // On télécharge les autoroutes
        var url = NSURL(string: "http://theurl.com")! // URL du JSON
        var request = NSURLRequest(URL: url) // Création de la requête HTTP
        var queue = NSOperationQueue()  // Création de NSOperationQueue à laquelle le bloc du gestionnaire est distribué lorsque la demande complète ou échoué

        // Envoi de la requête asynchrone en utilisant NSURLConnection
        NSURLConnection.sendAsynchronousRequest(request, queue: queue, completionHandler:{(response: NSURLResponse!, data: NSData!, error: NSError!) ->Void in
            // Gestion des erreurs de connexion
            if error != nil {
                // Masquer l'icône de chargement dans la barre de status
                UIApplication.sharedApplication().networkActivityIndicatorVisible = false

                println(error.localizedDescription)
                let errorAlert = UIAlertView(title: "Erreur", message: error.localizedDescription, delegate: self, cancelButtonTitle: "OK")
                errorAlert.show()
            }
            else {
                // Récupération du JSON et gestion des erreurs
                let json = JSON(data: data)

                if let highwaysData = json.arrayValue {
                    for highway in highwaysData {
                        var newHighway = Highway(ids: highway["Ids"].stringValue, name: highway["Name"].stringValue, label: highway["Direction"].stringValue, length: highway["Length"].stringValue, directions: highway["Direction"].stringValue, operateur: highway["Operator"].stringValue)
                        self.highways.append(newHighway)
                    }
                }
            }
        })

        if (self.refreshControl!.refreshing) {
            self.refreshControl!.endRefreshing()
        }

        tableView.reloadData() // Here is the problem

        // Masquer l'icône de chargement dans la barre de status
        UIApplication.sharedApplication().networkActivityIndicatorVisible = false
    }

    // MARK: - Table view data source

    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        // Return the number of rows in the section.
        return highways.count
    }

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

        // Configure the cell...
        let tableCell = highways[indexPath.row]

        let nameLabel = cell.viewWithTag(1) as UILabel
        let directionLabel = cell.viewWithTag(2) as UILabel

        nameLabel.text = tableCell.name!
        directionLabel.text = tableCell.getDirections()

        return cell
    }

    /*
    // MARK: - Navigation

    // In a storyboard-based application, you will often want to do a little preparation before navigation
    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
        // Get the new view controller using [segue destinationViewController].
        // Pass the selected object to the new view controller.
    }
    */

}
Florentin
  • 1,433
  • 2
  • 13
  • 22
  • 2
    Do you call `refresh()` only from `viewDidLoad`? Take a note that its only executed once per view controllers life cycle. – Kirsteins Nov 26 '14 at 20:32
  • Where are you calling reloadData aside from viewDidLoad? – Paulw11 Nov 26 '14 at 20:32
  • 1
    Also, there is no need to pass a reference to self inside the refresh method – Jason Renaldo Nov 26 '14 at 20:33
  • @Kirsteins no, I also call `refresh()` when the user "Pull to refresh" – Florentin Nov 26 '14 at 20:42
  • @Paulw11 in viewDidLoad: `refreshControl!.addTarget(self, action: Selector("refresh:"), forControlEvents: UIControlEvents.ValueChanged)` – Florentin Nov 26 '14 at 20:44
  • And how is refreshControl instantiate do? Is it in your storyboard do are you allocating it? Have you checked it isn't nil? – Paulw11 Nov 26 '14 at 20:46
  • @Paulw11 yes, my refreshControl is all good: `refreshControl = UIRefreshControl()` – Florentin Nov 26 '14 at 20:52
  • 1
    That allocates a refresh control, how do you put it into the tableView? Please show all relevant code in your question rather than having to have us tease it out a line at a time in comments – Paulw11 Nov 26 '14 at 20:59

2 Answers2

13

In your refresh function, your load completes asynchronously using a closure, but you are updating your activity indicator and reloading your table outside of the closure, so it will execute before the load is completed. You need to move this code inside the closure and ensure that it executes on the main queue (as it updates the UI)

func refresh(sender: AnyObject) {
        // Afficher l'icône de chargement dans la barre de status
        UIApplication.sharedApplication().networkActivityIndicatorVisible = true

        // On télécharge les autoroutes
        var url = NSURL(string: "http://theurl.com")! // URL du JSON
        var request = NSURLRequest(URL: url) // Création de la requête HTTP
        var queue = NSOperationQueue()  // Création de NSOperationQueue à laquelle le bloc du gestionnaire est distribué lorsque la demande complète ou échoué

        // Envoi de la requête asynchrone en utilisant NSURLConnection
        NSURLConnection.sendAsynchronousRequest(request, queue: queue, completionHandler:{(response: NSURLResponse!, data: NSData!, error: NSError!) ->Void in
            // Gestion des erreurs de connexion
            if error != nil {
                // Masquer l'icône de chargement dans la barre de status
                UIApplication.sharedApplication().networkActivityIndicatorVisible = false

                println(error.localizedDescription)
                let errorAlert = UIAlertView(title: "Erreur", message: error.localizedDescription, delegate: self, cancelButtonTitle: "OK")
                errorAlert.show()
            }
            else {
                // Récupération du JSON et gestion des erreurs
                let json = JSON(data: data)

                if let highwaysData = json.arrayValue {
                    for highway in highwaysData {
                        var newHighway = Highway(ids: highway["Ids"].stringValue, name: highway["Name"].stringValue, label: highway["Direction"].stringValue, length: highway["Length"].stringValue, directions: highway["Direction"].stringValue, operateur: highway["Operator"].stringValue)
                        self.highways.append(newHighway)
                    }
                }
            }

            dispatch_async(dispatch_get_main_queue(), {

                if (self.refreshControl!.refreshing) {
                    self.refreshControl!.endRefreshing()
                }

                self.tableView.reloadData() 

                // Masquer l'icône de chargement dans la barre de status
                UIApplication.sharedApplication().networkActivityIndicatorVisible = false
            })

        })

    }
Paulw11
  • 108,386
  • 14
  • 159
  • 186
  • Thank you very much, it works perfectly and now I understand better my mistake ! :) – Florentin Nov 28 '14 at 20:56
  • 1
    PS: in a closure we have to call the variable with `self`: `self.tableView.reloadData()` – Florentin Nov 28 '14 at 20:56
  • I have updated my answer. I just copied your code so I missed that. Personally I always use `self` to avoid confusion, also it is a habit from Obj C where it is always required. – Paulw11 Nov 28 '14 at 20:58
2

If you are using a tab bar controller (which it sounds like you are), and are expecting a refresh on the table after modifying something in another tab, you will need to call tableView.reloadData() from inside of the destination view controller's viewWillAppear() or viewDidAppear() method instead. viewDidLoad() is only called once for the life of that view controller.

GonzoCoder
  • 259
  • 2
  • 6
  • No, I'm expecting a refresh in the same tab. I just want my table view to refresh after loading data but it doesn't work – Florentin Nov 26 '14 at 21:52
  • Need to see more of the view controller code. "Loading data" could be done many different ways. – GonzoCoder Nov 26 '14 at 22:48