自定义UITableview单元偶尔重叠/再现在其他单元格的顶部

真的很奇怪,我在这里。

基本上,用户来到一个附有TableView的View。 有一些数据被调用到后端,并且在该数据的完成块中提供给TableView以显示。

在cellForRow 1 of 4可重用中,自定义单元基于某些逻辑出列,然后调用该单元的configure方法。

这里有些棘手:一些单元格可能需要进一步的异步调用(为自定义显示获取更多数据),因此需要一个tableView.reloadRows调用…然后,最好用这两个截图来解释:

错误 对

在第一个那个单元格中,“曾经想过你会为克朗代克酒吧做些什么吗?” 复制在“演员的自我管理”单元格之上,作为包含Twitter信息DID的单元格,需要额外的异步调用来获取该数据。

有什么想法吗? 我在begin / endUpdates块中运行tableView.reloadRows调用,但没有骰子。

cellForRow:

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let postAtIndexPath = posts[indexPath.row] let cell: PostCell! if postAtIndexPath.rawURL == "twitter.com" { cell = tableView.dequeueReusableCell(withIdentifier: "TwitterCell", for: indexPath) as! TwitterCell } else if !postAtIndexPath.rawURL.isEmpty { cell = tableView.dequeueReusableCell(withIdentifier: "LinkPreviewCell", for: indexPath) as! LinkPreviewCell } else if postAtIndexPath.quotedID > -1 { cell = tableView.dequeueReusableCell(withIdentifier: "QuoteCell", for: indexPath) as! QuoteCell } else { cell = tableView.dequeueReusableCell(withIdentifier: "PostCell", for: indexPath) as! PostCell } cell.isUserInteractionEnabled = true cell.privacyDelegate = self cell.shareDelegate = self cell.likeDelegate = self cell.linkPreviewDelegate = self cell.reloadDelegate = self postToIndexPath[postAtIndexPath.id] = indexPath if parentView == "MyLikes" || parentView == "Trending" { cell.privatePostButton.isHidden = true cell.privatePostLabel.isHidden = true } cell.configureFor(post: postAtIndexPath, liked: likesCache.postIsLiked(postId: postAtIndexPath.id), postIdToAttributions: postIdToAttributions, urlStringToOGData: urlStringToOGData, imageURLStringToData: imageURLStringToData) cell.contentView.layoutIfNeeded() return cell } 

asynch Twitter电话:

 private func processForTwitter(og: OpenGraph, urlLessString: NSMutableAttributedString, completion:(() -> ())?) { let ogURL = og[.url]! let array = ogURL.components(separatedBy: "/") let client = TWTRAPIClient() let tweetID = array[array.count - 1] if let tweet = twitterCache.tweet(forID: Int(tweetID)!) { for subView in containerView.subviews { subView.removeFromSuperview() } let tweetView = TWTRTweetView(tweet: tweet as? TWTRTweet, style: .compact) containerView.addSubview(tweetView) tweetView.translatesAutoresizingMaskIntoConstraints = false tweetView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor).isActive = true tweetView.topAnchor.constraint(equalTo: containerView.topAnchor).isActive = true containerView.leftAnchor.constraint(equalTo: tweetView.leftAnchor).isActive = true containerView.rightAnchor.constraint(equalTo: tweetView.rightAnchor).isActive = true containerView.isHidden = false postText.isHidden = false } else { 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)!) } } if let complete = completion { complete() } })`enter code here` } if urlLessString.string.isEmpty { if let ogTitle = og[.title] { postText.text = String(htmlEncodedString: ogTitle) } } else { postText.attributedText = urlLessString } } 

tableviewcontroller中的完成块:

 func reload(postID: Int) { tableView.beginUpdates() self.tableView.reloadRows(at: [self.postToIndexPath[postID]!], with: .automatic) tableView.endUpdates() } 

注意:这种情况不会100%发生,大概是网络依赖的。

我想问题在于计算单元格中存在的组件的大小。 就像单元格被设计一样,框架以及子视图在数据可用之前设置。

也许你可以尝试,

tableView.rowHeight = UITableViewAutomaticDimension tableView.estimatedRowHeight = yourPrefferedHeight

但是你需要确保自动布局约束是正确的,如果所有组件都有高度,单元格可以确定它的高度。

我假设在configureFor background / async上processForTwitter 。 如果是,这就是错误。 您的api调用应该在后台,但如果您从缓存中获取推文,则应该在主线程上和返回单元格之前完成addSubview。 尝试这样做,确保在主线程上processForTwitter并将其配置为

  private func processForTwitter(og: OpenGraph, urlLessString: NSMutableAttributedString, completion:(() -> ())?) { let ogURL = og[.url]! let array = ogURL.components(separatedBy: "/") let client = TWTRAPIClient() let tweetID = array[array.count - 1] if let tweet = twitterCache.tweet(forID: Int(tweetID)!) { for subView in containerView.subviews { subView.removeFromSuperview() } let tweetView = TWTRTweetView(tweet: tweet as? TWTRTweet, style: .compact) containerView.addSubview(tweetView) tweetView.translatesAutoresizingMaskIntoConstraints = false tweetView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor).isActive = true tweetView.topAnchor.constraint(equalTo: containerView.topAnchor).isActive = true containerView.leftAnchor.constraint(equalTo: tweetView.leftAnchor).isActive = true containerView.rightAnchor.constraint(equalTo: tweetView.rightAnchor).isActive = true containerView.isHidden = false postText.isHidden = false } else { // run only this code in background/async 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)!) } } // completion on main if let complete = completion { DispatchQueue.main.async { complete() } } }) } if urlLessString.string.isEmpty { if let ogTitle = og[.title] { postText.text = String(htmlEncodedString: ogTitle) } } else { postText.attributedText = urlLessString } } 

现在序列就像这样

  1. Cell首次加载
  2. 在缓存中找不到推文
  3. processForTwitter异步网络调用和单元格在没有适当数据的情况下返回
  4. 异步调用得到推文。 并在主线程上调用完成,从而导致重新加载
  5. 在主线程上再次调用configure for
  6. 在保留主线程时,在缓存中找到tweet。
  7. 添加你的TWTRTweetView(你还在主线程上),这将在你的单元格返回之前完成。

记住这一点,在CellForIndexPath中返回单元格之前,应该在主线程上添加单元格中用于计算单元格大小的每个组件。

在异步调用之后,而不是tableView.reloadRows尝试调用:

 tableView.beginUpdates() tableView.setNeedsLayout() tableView.endUpdates() 

似乎数据已填充,但单元格的大小不会反映出来。 如果你调用reloadRows我担心它不会重新计算以下单元格的新位置 – 这意味着如果单元格行3调整到更大的高度,它将变得更大,但是下面的单元格不会被进一步向下推。

结果发现有一些奇怪的,或者至少我不明白,在estimatedRowHeightreloadRows周围的行为; 解决方案,通过切向相关的SO问题,缓存行高,因为它们变得可用,并返回如果在heightForRow可用。

我想它不会重新计算重载时的高度或其他东西? 真的不明白为什么这个工作,但我很高兴它!

我遇到了类似的问题。 后

  [self.tableView beginUpdates]; [self configureDataSource]; // initialization of cells by dequeuing [self.tableView insertSections:[[NSIndexSet alloc] initWithIndex:index] withRowAnimation:UITableViewRowAnimationFade]; [self.tableView endUpdates]; 

以及之后

  [self.tableView beginUpdates]; [self configureDataSource]; // initialization of cells by dequeuing [self.tableView deleteSections:[NSIndexSet indexSetWithIndex:index] withRowAnimation:UITableViewRowAnimationFade]; [self.tableView endUpdates]; 

我的TableViewController的两行重叠。

然后我发现了捕获的位置。

在beginUpdate和endUpdate之后是两个为单元格调用的方法:estimatedHeightForRowAtIndexPath和heightForRowAtIndexPath。 但是没有调用cellForRowAtIndexPath。

其中一个重叠单元格已出列但我只在cellForRowAtIndexPath方法中设置了它的文本/值。 因此,正确计算了单元格高度但是内容不正确。

我的解决方法是在将单元格出列后也设置文本/值。 所以唯一的区别在于我的configureDataSource方法:

  [self.tableView beginUpdates]; [self configureDataSource]; // init of cells by dequeuing and set its texts/values // delete or insert section [self.tableView endUpdates]; 

(其他可能的解决方法是避免细胞重新初始化。但是这种初始化细胞的缓存对于具有大量细胞的TableView来说并不好)