it-swarm.com.de

self.tableView.reloadData () funktioniert nicht in Swift

Ich versuche gleichzeitig, Swift & die Grundlagen von iOS dev zu lernen. Ich habe eine TableViewController, die zunächst eine lokale JSON-Datei analysiert und sehr einfache Daten in TableViewCell und SectionHeaderViews rendert. Innerhalb desselben TableViewController rufe ich einen JSON-Endpunkt an, der Daten zurückgibt, die ich dann auf Variablen setze, damit ich auf das zugreifen kann, auf das ich eigentlich zugreifen möchte (die API-Struktur ist nicht wünschenswert). Also habe ich endlich die richtigen Daten als self.tableData eingestellt und dann self.tableView.reloadData() aufgerufen, aber es passiert nichts. Was gibt?

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

Sie müssen die Tabelle erneut in den Thread UI laden:

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

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

Follow-up: Eine einfachere Alternative zum connection.start()-Ansatz ist die Verwendung von 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()
}

Dies ermöglicht Ihnen jedoch nicht die Flexibilität der Nachverfolgung der Bytes. Beispielsweise möchten Sie den Fortschritt des Downloads über bytesDownloaded/bytesNeeded berechnen

153
noobular

Sie müssen nur eingeben:

Zuerst ein IBOutlet:

@IBOutlet var appsTableView : UITableView

Dann in einer Action-Funktion:

self.appsTableView.reloadData()
20
user3752603

Wenn Ihre Verbindung im Hintergrund-Thread ist, sollten Sie die Benutzeroberfläche im Haupt-Thread wie folgt aktualisieren

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

Wie ich hier erwähnt habe

Schnell 4:

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

In meinem Fall wurde die Tabelle korrekt aktualisiert, aber setNeedsDisplay () wurde nicht für das Image aufgerufen. Ich dachte irrtümlicherweise, dass die Daten nicht neu geladen wurden.

2
Dominique Lorre

Das Problem war also, dass ich @lazy unangemessen verwenden wollte, was dazu führte, dass meine Business-Variable im Wesentlichen eine Konstante war und daher nicht editierbar war. Statt den lokalen Json zu laden, lade ich jetzt nur die von der API zurückgegebenen Daten.

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 auf @lazy

Sie müssen eine Lazy-Eigenschaft immer als Variable deklarieren (mit dem Schlüsselwort var), da ihr Anfangswert möglicherweise erst nach Abschluss der Instanzinitialisierung abgerufen wird. Konstante Eigenschaften müssen vor der Initialisierung immer einen Wert haben und können daher nicht als Lazy deklariert werden.

1
chandlervdw

Neben den offensichtlichen reloadData von UI/Main Thread (wie auch immer Apple es bezeichnet) hatte ich in meinem Fall vergessen, auch die SECTIONS-Informationen zu aktualisieren. Daher wurden keine neuen Abschnitte erkannt!

1
Radu

Sie müssen Ihre TableView nur in main thread neu laden. Andernfalls wird Ihre App abgestürzt oder nach einiger Zeit aktualisiert. Für jedes UI-Update wird empfohlen, den Haupt-Thread zu verwenden. 

//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

Alle Aufrufe der Benutzeroberfläche sollten asynchron sein. Alles, was Sie auf der Benutzeroberfläche ändern, z. B. das Aktualisieren der Tabelle oder das Ändern der Beschriftung, sollten Sie über den Hauptthread ausführen. Mit DispatchQueue.main wird Ihre Operation der Warteschlange im Hauptthread hinzugefügt. 

Schnell 4

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