Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

cellForRowAtIndexPath not called after reloadData but numberOfRowsInSection does

In my current project I'm trying to fetch news from a REST-Endpoint and I wanna refresh the UITableView once the network request is finished. So I call reloadData() but unfortunately cellForRowAtIndexPath isn't called however numberOfRowsInSection gets called and returns the correct number of items.

Following my code stripped down to the relevant parts:

class NewsTableViewController: UIViewController {

    private var newsItems: Array<NewsItem>!
    private var tableView: UITableView!

    override func viewDidLoad() {
        super.viewDidLoad()
        newsItems = []
        addDummyItem()
        prepareTableView()
        getNews()
    }

    func addDummyItem(){
        let newsItem = NewsItem()
        newsItem.dateString = "12th May"
        newsItem.title = "Lorem ipsum"
        newsItem.imgUrl = "URL"
        newsItem.url = "URL"
        newsItem.message = "Lorem ipsum dolor sit amet"
        self.newsItems.append(newsItem)
    }

    func getNews(){
        let url = NSURL(string: "http://someurl.com/news")
        let urlRequest: NSMutableURLRequest = NSMutableURLRequest(URL: url!)
        let session = NSURLSession.sharedSession()
        let task = session.dataTaskWithRequest(urlRequest) { data, response, error in
            guard data != nil && error == nil else {
                print(error)
                return
            }

            guard let httpResponse = response as? NSHTTPURLResponse where httpResponse.statusCode == 200 else {
                print("status code not 200; \(response)")
                return
            }
            let json = String(data:data!, encoding: NSISOLatin1StringEncoding)
            let arr: AnyObject? = json?.parseJSONString
            for item in arr as! Array<AnyObject>{
                let newsItem = NewsItem()
                newsItem.dateString = (item["dateString"] as AnyObject? as? String) ?? ""
                newsItem.title = (item["title"] as AnyObject? as? String) ?? ""
                newsItem.imgUrl = (item["imgUrl"] as AnyObject? as? String) ?? ""
                newsItem.url = (item["url"] as AnyObject? as? String) ?? ""
                newsItem.message = (item["message"] as AnyObject? as? String) ?? ""
                self.newsItems.append(newsItem)
            }
            print("Network request finished - call reloadData()")
            dispatch_async(dispatch_get_main_queue(), {
               self.tableView.reloadData()
            })
        }
        task.resume()
    }

    override func viewWillAppear(animated: Bool) {
        super.viewWillAppear(animated)
        automaticallyAdjustsScrollViewInsets = false
    }

    private func prepareTableView() {
        tableView = UITableView()
        tableView.dataSource = self
        tableView.delegate = self
        tableView.separatorColor = UIColor.clearColor()
    }
}

extension NewsTableViewController : UITableViewDataSource{

    func numberOfSectionsInTableView(tableView: UITableView) -> Int {
        return 1
    }

    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        print("Item count: ", newsItems.count)
        return newsItems.count
    }

    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        print("cellForRowAtIndexPath called with indexPathRow: ", indexPath.row)
        let cell = tableView.dequeueReusableCellWithIdentifier("ImageCardViewCell") as! NewsImageCardViewCell
        let item: NewsItem = newsItems[indexPath.row]
        getImageCardView(item.url, btnDate: item.dateString, textTitle: item.title, textDetail: item.message, imageCardView: cell.imageCardView, imageName: "news_1", indexPath: indexPath)
        return cell
    }
}

extension NewsTableViewController : UITableViewDelegate{
    func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
        tableView.deselectRowAtIndexPath(indexPath, animated: true)
    }
}

As you can see: The UITableView is correctly setup by setting the dataSource and delegate to my class. The reloadData() method is called on the main thread. Following the log-output:

Item count:  1
cellForRowAtIndexPath called with indexPathRow:  0
Network request finished - call reloadData()
Item count:  72

Note: At first there's one dummy item my array just to show you that cellForRowAtIndexPath is indeed called but only one time. After triggering reloadData() on numberOfRowsInSection is called but not the cellForRowAtIndexPath method.

I know that there're a lot of similar issues like this and I've checked them all for possible solutions but none were working. I appreciate any help.

Edit

Please note that the dummy view which is added before the network request is visible. So the UITableViews' height is not 0.

Edit #2

Thanks to everyone for your answers. I've found the issue. I've instantiated the ViewController via the Storyboard but I missed to set an IBOutlet. Due that the bounds of my UITableView were 0,0,0,0.

like image 230
user3420815 Avatar asked Dec 14 '22 04:12

user3420815


1 Answers

That because your table view did not add to your window, If the table view is not visible, the UIKit will not deal with UI related methods. It's kind of Apple's lazy load.
Change your prepareTableView function as below:

private func prepareTableView() {
    tableView = UITableView(frame: self.view.bounds, style: .Plain)
    tableView.dataSource = self
    tableView.delegate = self
    tableView.separatorColor = UIColor.clearColor()
    self.view.addSubview(tableView)
}
like image 185
zylenv Avatar answered May 21 '23 04:05

zylenv