Nick Coelius
Nick Coelius

Reputation: 4746

UITableView Floating Cell After Async Fetch

This is in the same vein as a previous question I here

Basically, UITableView cells would occasionally overlap the data underneath them - I tracked that down to reloadRows acting wonky with estimatedHeight, and my solve was to cache the height when calling willDisplay cell: and then return that height, or an arbitrary constant if the row hasn't been seen yet, when calling heightForRow

But now the problem is back! Well, a similar one: after propagating a UITableView with some data, some of it fetched asynchronously, I want to be able to search and repopulate the UITableView.

This data I'm fetching may or may not already be present on the TableView, and in any case I don't consider that - I hit the backend, grab some stuff, and display it.

Except, it gets wonky:

enter image description here

As you can see from the screenshot, there's a cell overlaid on top of another cell with the same content. The TableView only reports there being 2 rows, via numberOfRows, but the View Hierarchy says there are 3 cells present when I click through to the TableView.

Only thing I can figure is there's some weird race condition or interaction that happens when I reloadRow after fetching the openGraph data.

What gives?

Some code:

Search

fileprivate func search(searchText: String, page: Int) {
    postsService.searchPosts(searchText: searchText, page: page) { [weak self] posts, error in
        if let weakSelf = self {
            if let posts = posts, error == nil {
                if !posts.isEmpty {
                    weakSelf.postListController.configureWith(posts: posts, deletionDelegate: nil, forParentView: "Trending")
                    weakSelf.page = weakSelf.page + 1
                    weakSelf.scrollTableViewUp()
                } else {
                    // manually add a "No results found" string to tableFooterView
                }
            } else {
                weakSelf.postListController.configureWith(posts: weakSelf.unfilteredPosts, deletionDelegate: nil, forParentView: "Trending")
                weakSelf.scrollTableViewUp()
            }
        }
    }
}

**configureWith*

func configureWith(posts: [Post], deletionDelegate: DeletionDelegate?, forParentView: String) {
    self.posts = posts

    for post in posts {
        //some data pre-processing
        if some_logic
            if rawURLString.contains("twitter") {
                let array = rawURLString.components(separatedBy: "/")
                let client = TWTRAPIClient()
                let tweetID = array[array.count - 1]
                client.loadTweet(withID: tweetID, completion: { [weak self] (t, error) in
                    if let weakSelf = self {
                        if let tweet = t {
                            weakSelf.twitterCache.addTweetToCache(tweet: tweet, forID: Int(tweetID)!)
                        }
                    }
                })
            }

            openGraphService.fetchOGData(url: rawURL, completion: { [weak self] (og, error) in
                weakSelf.openGraphService.fetchOGImageData(url: ogImageURL, completion: { (data, response, error) in
                    if let imageData = data {
                        weakSelf.imageURLStringToData[ogImageString] = imageData
                        weakSelf.queueDispatcher.dispatchToMainQueue {
                            for cell in weakSelf.tableView.visibleCells {
                                if (cell as! PostCell).cellPost == post {
                                    let cellIndexPath = IndexPath(row: weakSelf.posts.index(of: post)!, section: 0)
                                    weakSelf.tableView.reloadRows(at: [cellIndexPath], with: UITableViewRowAnimation.automatic)
                                }
                            }
                        }
                    }
                })
            })
    }
    self.deletionDelegate = deletionDelegate
    self.parentView = forParentView

    queueDispatcher.dispatchToMainQueue { [weak self] in
        if let weakSelf = self {
            weakSelf.tableView.reloadData()
        }
    }

    scrollToPost()
}

Upvotes: 1

Views: 109

Answers (0)

Related Questions