web-dev-qa-db-fra.com

self.tableView.reloadData () ne fonctionne pas dans Swift

J'essaie d'apprendre Swift & les bases de iOS dev en même temps, alors supportez-moi. J'ai une TableViewController qui analyse d'abord un fichier JSON local et rend ses données très simples en TableViewCell et SectionHeaderViews. Dans la même TableViewController, j'appelle un noeud final JSON, qui renvoie des données, que je mets ensuite en variables afin que je puisse accéder à ce que je veux vraiment obtenir (la structure de l'API n'est pas souhaitable). Donc, j'ai finalement défini les données appropriées pour être self.tableData et ensuite appeler self.tableView.reloadData() mais rien ne se passe. Ce qui donne?

import UIKit

class BusinessTableViewController: UITableViewController {

    var data: NSMutableData = NSMutableData()
    var tableData: NSArray = NSArray()

    @lazy var Business: NSArray = {
        let pathTCT = NSBundle.mainBundle().pathForResource("TCT", ofType: "json")
        let data = NSData.dataWithContentsOfFile(pathTCT, options: nil, error: nil)
        return NSJSONSerialization.JSONObjectWithData(data, options: nil, error: nil) as NSArray
        }()

    override func viewDidLoad() {
        super.viewDidLoad()

        navigationItem.titleView = UIImageView(image: UIImage(named: "growler"))

        tableView.registerClass(BeerTableViewCell.self, forCellReuseIdentifier: "cell")
        tableView.separatorStyle = .None

        fetchKimono()
    }

    override func numberOfSectionsInTableView(tableView: UITableView!) -> Int {
//        return Business.count
        return 1
    }

    override func tableView(tableView: UITableView?, numberOfRowsInSection section: Int) -> Int {
        let biz = Business[section] as NSDictionary
        let results = biz["results"] as NSDictionary
        let beers = results["collection1"] as NSArray
        return beers.count
    }

    override func tableView(tableView: UITableView?, cellForRowAtIndexPath indexPath: NSIndexPath?) -> UITableViewCell? {
        let cell = tableView!.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath!) as BeerTableViewCell
        if let path = indexPath {
            let biz = Business[path.section] as NSDictionary
            let results = biz["results"] as NSDictionary
            let beers = results["collection1"] as NSArray
            let beer = beers[path.row] as NSDictionary

            cell.titleLabel.text = beer["BeerName"] as String
        }

        return cell
    }

    override func tableView(tableView: UITableView!, titleForHeaderInSection section: Int) -> String! {
        let biz = Business[section] as NSDictionary
        return biz["name"] as String
    }

    override func tableView(tableView: UITableView!, viewForHeaderInSection section: Int) -> UIView! {
        let biz = Business[section] as NSDictionary
        let view = LocationHeaderView()
        view.titleLabel.text = (biz["name"] as String).uppercaseString
        return view
    }

    override func tableView(tableView: UITableView!, heightForHeaderInSection section: Int) -> CGFloat {
        return 45
    }

    func fetchKimono() {
        var urlPath = "names have been changed to protect the innocent"
        var url: NSURL = NSURL(string: urlPath)
        var request: NSURLRequest = NSURLRequest(URL: url)
        var connection: NSURLConnection = NSURLConnection(request: request, delegate: self, startImmediately: false)

        connection.start()
    }

    func connection(didReceiveResponse: NSURLConnection!, didReceiveResponse response: NSURLResponse!) {
        // Recieved a new request, clear out the data object
        self.data = NSMutableData()
    }

    func connection(connection: NSURLConnection!, didReceiveData data: NSData!) {
        // Append the recieved chunk of data to our data object
        self.data.appendData(data)
    }

    func connectionDidFinishLoading(connection: NSURLConnection!) {
        // Request complete, self.data should now hold the resulting info
        // Convert the retrieved data in to an object through JSON deserialization
        var err: NSError
        var jsonResult: NSDictionary = NSJSONSerialization.JSONObjectWithData(data, options:    NSJSONReadingOptions.MutableContainers, error: nil) as NSDictionary
        var results: NSDictionary = jsonResult["results"] as NSDictionary
        var collection: NSArray = results["collection1"] as NSArray
        if jsonResult.count>0 && collection.count>0 {
            var results: NSArray = collection as NSArray
            self.tableData = results
            self.tableView.reloadData()
        }
    }
}
53
chandlervdw

Vous devrez recharger la table sur le thread UI via:

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

//Swift 3
DispatchQueue.main.async{
    self.tableView.reloadData()
}

Suivi: Une alternative plus facile à l’approche connection.start() consiste à utiliser plutôt NSURLConnection.sendAsynchronousRequest(...)

//NSOperationQueue.mainQueue() is the main thread
NSURLConnection.sendAsynchronousRequest(NSURLRequest(URL: url), queue: NSOperationQueue.mainQueue()) { (response, data, error) -> Void in
    //check error
    var jsonError: NSError?
    let json: AnyObject? = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.allZeros, error: &jsonError)
    //check jsonError
    self.collectionView?.reloadData()
}

Cela ne vous donne cependant pas la possibilité de suivre les octets. Vous pouvez par exemple vouloir calculer la progression du téléchargement via bytesDownloaded/bytesNeeded

153
noobular

Vous avez juste à entrer:

D'abord un IBOutlet:

@IBOutlet var appsTableView : UITableView

Puis dans une action func:

self.appsTableView.reloadData()
20
user3752603

Si votre connexion est en tâche de fond, vous devez mettre à jour l'interface utilisateur dans le fil principal comme ceci:

self.tblMainTable.performSelectorOnMainThread(Selector("reloadData"), withObject: nil, waitUntilDone: true)

Comme je l'ai mentionné ici

Swift 4:

self.tblMainTable.performSelector(onMainThread: #selector(UICollectionView.reloadData), with: nil, waitUntilDone: true)
4
Anand Suthar

Dans mon cas, la table a été mise à jour correctement, mais setNeedsDisplay () n'étant pas appelée pour l'image, j'ai pensé à tort que les données n'étaient pas rechargées.

2
Dominique Lorre

Le problème était donc que j’essayais d’utiliser de manière inappropriée @lazy, ce qui faisait que ma variable Business était essentiellement constante et donc non modifiable. De plus, au lieu de charger le json local, je ne charge plus que les données renvoyées par l'API.

import UIKit

class BusinessTableViewController: UITableViewController {

    var data: NSMutableData = NSMutableData()
    var Business: NSMutableArray = NSMutableArray()

    override func viewDidLoad() {
        super.viewDidLoad()

        navigationItem.titleView = UIImageView(image: UIImage(named: "growler"))

        tableView.registerClass(BeerTableViewCell.self, forCellReuseIdentifier: "cell")
        tableView.separatorStyle = .None

        fetchKimono()
    }

    override func numberOfSectionsInTableView(tableView: UITableView!) -> Int {
        return Business.count
    }

    override func tableView(tableView: UITableView?, numberOfRowsInSection section: Int) -> Int {
        if (Business.count > 0) {
            let biz = Business[section] as NSDictionary
            let beers = biz["results"] as NSArray
            return beers.count
        } else {
            return 0;
        }
    }

    override func tableView(tableView: UITableView?, cellForRowAtIndexPath indexPath: NSIndexPath?) -> UITableViewCell? {
        let cell = tableView!.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath!) as BeerTableViewCell
        if let path = indexPath {
            let biz = Business[path.section] as NSDictionary
            let beers = biz["results"] as NSArray
            let beer = beers[path.row] as NSDictionary

            cell.titleLabel.text = beer["BeerName"] as String
        } else {
            cell.titleLabel.text = "Loading"
        }

        return cell
    }

    override func tableView(tableView: UITableView!, viewForHeaderInSection section: Int) -> UIView! {
        let view = LocationHeaderView()
        let biz = Business[section] as NSDictionary
        if (Business.count > 0) {
            let count = "\(Business.count)"
            view.titleLabel.text = (biz["name"] as String).uppercaseString
        }
        return view
    }

    override func tableView(tableView: UITableView!, heightForHeaderInSection section: Int) -> CGFloat {
        return 45
    }

    func fetchKimono() {
        var urlPath = "names have been removed to protect the innocent"
        var url: NSURL = NSURL(string: urlPath)
        var request: NSURLRequest = NSURLRequest(URL: url)
        var connection: NSURLConnection = NSURLConnection(request: request, delegate: self, startImmediately: false)

        connection.start()
    }

    func connection(didReceiveResponse: NSURLConnection!, didReceiveResponse response: NSURLResponse!) {
        // Recieved a new request, clear out the data object
        self.data = NSMutableData()
    }

    func connection(connection: NSURLConnection!, didReceiveData data: NSData!) {
        // Append the recieved chunk of data to our data object
        self.data.appendData(data)
    }

    func connectionDidFinishLoading(connection: NSURLConnection!) {
        // Request complete, self.data should now hold the resulting info
        // Convert the retrieved data in to an object through JSON deserialization
        var err: NSError
        var jsonResult: NSDictionary = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: nil) as NSDictionary
        var results: NSDictionary = jsonResult["results"] as NSDictionary
        var collection: NSArray = results["collection1"] as NSArray
        if jsonResult.count>0 && collection.count>0 {
            Business = jsonResult
            tableView.reloadData()
        }
    }
}

Swift Docs sur @lazy

Vous devez toujours déclarer une propriété paresseuse en tant que variable (avec le mot-clé var), car sa valeur initiale ne peut pas être extraite avant la fin de l'initialisation de l'instance. Les propriétés constantes doivent toujours avoir une valeur avant la fin de l'initialisation et ne peuvent donc pas être déclarées paresseuses.

1
chandlervdw

Outre le reloadData évident de UI/Main Thread (quel que soit le nom d’Apple), dans mon cas, j’avais oublié de mettre à jour les informations SECTIONS. Par conséquent, il n'a pas détecté de nouvelles sections!

1
Radu

Vous devez recharger votre TableView dans thread principal uniquement. Sinon, votre application sera bloquée ou sera mise à jour après un certain temps. Pour chaque mise à jour de l'interface utilisateur, il est recommandé d'utiliser le fil principal. 

//To update UI only this below code is enough
//If you want to do changes in UI use this
DispatchQueue.main.async(execute: {
    //Update UI
    self.tableView.reloadData()//Your tableView here
})

//Perform some task and update UI immediately.
DispatchQueue.global(qos: .userInitiated).async {  
    // Call your function here
    DispatchQueue.main.async {  
        // Update UI
        self.tableView.reloadData()  
    }
}

//To call or execute function after some time and update UI
DispatchQueue.main.asyncAfter(deadline: .now() + 5.0) {
    //Here call your function
    //If you want to do changes in UI use this
    DispatchQueue.main.async(execute: {
        //Update UI
        self.tableView.reloadData()
    })
}
0
iOS

Tous les appels à l'interface utilisateur doivent être asynchrones, tout ce que vous changez sur l'interface utilisateur, comme la mise à jour de la table ou la modification du libellé du texte, doit être effectué à partir du thread principal . 

Swift 4

DispatchQueue.main.async{
    self.tableView.reloadData()
}
0
makkhokher