Подтвердить что ты не робот

Self.tableView.reloadData() не работает в Swift

Я пытаюсь изучить Swift и основы разработки для iOS одновременно, так что терпите меня. У меня есть TableViewController который сначала анализирует локальный файл JSON и отображает его очень простые данные в TableViewCell и SectionHeaderViews. Внутри того же TableViewController я TableViewController вызов конечной точки JSON, которая возвращает данные, которые я затем устанавливаю для переменных, чтобы получить доступ к тому, к чему я на самом деле хочу добраться (структура API менее желательна). Итак, я наконец установил правильные данные как self.tableData а затем вызвал self.tableView.reloadData() но ничего не происходит. Что дает?

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()
        }
    }
}
4b9b3361

Ответ 1

Вам нужно будет перезагрузить таблицу в потоке UI через:

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

//swift 5
DispatchQueue.main.async{
    self.tableView.reloadData()
}

Продолжение: более простая альтернатива подходу connection.start() - вместо этого использовать 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()
}

Это не позволяет вам гибко отслеживать байты, хотя, например, вы можете рассчитать ход загрузки с помощью bytesDownloaded/bytesNeeded

Ответ 2

Вам нужно только ввести:

Сначала выберите IBOutlet:

@IBOutlet var appsTableView : UITableView

Затем в Action func:

self.appsTableView.reloadData()

Ответ 3

Если ваше соединение в фоновом потоке, то вы должны обновить пользовательский интерфейс в основном потоке, как это

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

Как я уже упоминал здесь

Свифт 4:

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

Ответ 4

В моем случае таблица была обновлена ​​правильно, но setNeedDisplay() не был вызван для изображения, поэтому я ошибочно считал, что данные не были перезагружены.

Ответ 5

Итак, проблема заключалась в том, что я пытался ненадлежащим образом использовать @lazy, что привело к тому, что моя бизнес-переменная была по существу постоянной и, следовательно, неизлечимой. Кроме того, вместо загрузки локального json я теперь загружаю только данные, возвращенные из 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 на @lazy:

Вы всегда должны объявлять ленивое свойство как переменную (с ключевым словом var), поскольку ее начальное значение не может быть получено до завершения инициализации экземпляра. Постоянные свойства всегда должны иметь значение до завершения инициализации и поэтому не могут быть объявлены ленивыми.

Ответ 6

Помимо очевидной reloadData из UI/Main Thread (что бы ни называл ее Apple), в моем случае я забыл также обновить информацию SECTIONS. Поэтому он не обнаружил никаких новых разделов!

Ответ 7

Все вызовы пользовательского интерфейса должны быть асинхронными, все, что вы изменяете в пользовательском интерфейсе, например обновление таблицы или изменение текстовой метки, должно выполняться из основного потока. использование DispatchQueue.main добавит вашу операцию в очередь в главном потоке.

Swift 4

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

Ответ 8

Вы должны перезагрузить ваш TableView только в основном потоке. В противном случае ваше приложение будет сбой или будет обновлено через некоторое время. Для каждого обновления пользовательского интерфейса рекомендуется использовать основной поток.

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

Ответ 9

Я также столкнулся с той же проблемой, что я сделал неправильно, что я забыл добавить

tableView.delegate = self    
tableView.dataSource = self

в методе viewDidLoad() {}. Это может быть одной из причин того, что self.tableView.reloadData() не работает.