table 加载时 TableView 自定义单元格 "jumps" 内的自定义 UIView

Custom UIView inside TableView Custom Cell "jumps" on table load

我有一个 UITableView,其中包含名为 NewsCell 的自定义单元格。 在 NewsCell 中,我有一个自定义 UIView 用作操作按钮。

很多时候 TableView 正在加载单元格时,自定义操作按钮会出现在单元格的 顶部 几秒钟,直到它返回到它在单元格底部的正确位置。有时它 returns 只有在我将单元格滚动出视口后它才会到达正确的位置。

知道为什么会这样吗?

这是一张让事情更清楚的照片: action button out of place

NewsCell 是使用 .xib 文件创建的,这里是相关代码:

protocol NewsCellDelegate {
    func favoriteIconDidPress(forArticle article: Article)
    func actionButtonDidPress(inside article: Article)
}

enum ArticleFavoriteMark {
    case selected
    case notSelected
}

class NewsCell: UITableViewCell, MainActionButtonDelegate{

    @IBOutlet weak var entireNewsCell: UIView!
    @IBOutlet weak var newsImage: UIImageView!
    @IBOutlet weak var favoriteIcon: UIImageView!
    
    @IBOutlet weak var dateLabel: UILabel!
    @IBOutlet weak var subjectTag: UIButton!
    @IBOutlet weak var moreSubjectsTag: UIButton!
    
    @IBOutlet weak var titleLabel: UILabel!
    @IBOutlet weak var authorLabel: UILabel!
    @IBOutlet weak var summaryLabel: UILabel!
    
    @IBOutlet weak var actionButton: MainActionButtonView!
    
    var delegate: NewsCellDelegate?
    var articleUrl = ""
    var articleID = ""
    var articleImageUrl = ""
    var isFavorite: Bool = false
    
    
    override func awakeFromNib() {
        super.awakeFromNib()

        setCellBorder()
        setCellColorsDesign()
        setImageRounded()
        setTagsRounded()
        setGestureRecognizer()
        
        actionButton.delegate = self
    }
    
    override func layoutSubviews() {
        super.layoutSubviews()

        contentView.frame = contentView.frame.inset(by: UIEdgeInsets(top: 20, left: 0, bottom: 0, right: 0))
    }
     

    func setGestureRecognizer() {
        favoriteIcon.addGestureRecognizer(UITapGestureRecognizer(target: favoriteIcon, action: #selector(favoriteIconPressed)))
        favoriteIcon.isUserInteractionEnabled = true
        let tapGestureRecognizer1 = UITapGestureRecognizer(target: self, action: #selector(favoriteIconPressed(tapGestureRecognizer:)))
        favoriteIcon.addGestureRecognizer(tapGestureRecognizer1)
    }
    
    
    @objc func favoriteIconPressed(tapGestureRecognizer: UITapGestureRecognizer) {
        let currentArticle = Article(id: articleID, articleTitle: titleLabel.text!, date: dateLabel.text ?? "", url: articleUrl, content: summaryLabel.text!, author: authorLabel.text ?? "", topic: subjectTag.currentTitle!, imageUrl: articleImageUrl , isFavorite: isFavorite)
        delegate?.favoriteIconDidPress(forArticle: currentArticle)
    }
    
    
    func actionButtonDidPress(btnText: String) {
        let currentArticle = Article(
            id: articleID,
            articleTitle: titleLabel.text ?? "",
            date: dateLabel.text ?? "",
            url: articleUrl,
            content: summaryLabel.text ?? "",
            author: authorLabel.text ?? "",
            topic: subjectTag.currentTitle ?? "",
            imageUrl: articleImageUrl,
            isFavorite: self.isFavorite)

        delegate?.actionButtonDidPress(inside: currentArticle)
    }
}

这是我的 NewsCell 元素层次结构:

news cell elements

这些是我对 NewsCell:

中的操作按钮施加的约束

constraints on action button inside NewsCell

这就是我限制 ActionButton UIView 的方式: constraints inside the action button custom uiview

在我的 TableView 中,我像这样启动每个单元格:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        
        let currentCell = tableView.dequeueReusableCell(withIdentifier: Constants.TableCellsIdentifier.FAVORITES, for: indexPath) as! SavedArticleCell
        currentCell.delegate = self
        
        let currentKey = viewModel.keysArray[indexPath.row]
        let savedArticle = viewModel.savedArticles[currentKey]
        
        if let savedArticle = savedArticle {
            currentCell.articleID = savedArticle.id!
            currentCell.articleTitle.text = savedArticle.title
            currentCell.articleTopic.setTitle(savedArticle.topic, for: .normal)
            
            if let imageUrl = savedArticle.imageUrl {
                currentCell.articleImageURL = imageUrl
                currentCell.articleImage.sd_setImage(with: URL(string: imageUrl), placeholderImage: UIImage(named: "light-gray-background"))
            } else {
                currentCell.articleImage.image = UIImage(named: "light-gray-background")
            }
        }
        
        return currentCell
    }

您必须在 NewsCell

layoutSubViews() 方法中添加 contentView.layoutIfNeeded()
override func layoutSubviews() {
    super.layoutSubviews()
    
    contentView.frame = contentView.frame.inset(by: UIEdgeInsets(top: 20, left: 0, bottom: 0, right: 0))
    contentView.layoutIfNeeded()
}

layoutIfNeeded() 的目的是强制同步(本质上是“立即”)重绘视图及其子视图。您必须调用它,因为您已经在 layoutsubviews() 方法中更新了布局。关于 layoutIfNeeded() 这是 Apple 所说的。

Use this method to force the view to update its layout immediately. When using Auto Layout, the layout engine updates the position of views as needed to satisfy changes in constraints. Using the view that receives the message as the root view, this method lays out the view subtree starting at the root. If no layout updates are pending, this method exits without modifying the layout or calling any layout-related callbacks.