【问题标题】:UITableViewCell constraints not updated till I scroll tableviewUITableViewCell 约束在我滚动 tableview 之前不会更新
【发布时间】:2026-01-18 13:35:01
【问题描述】:

以下是我在 tableview 单元格中更新视图高度的代码

 override func layoutSubviews() {
    super.layoutSubviews()
    layout()
}

fileprivate func layout() {
    rootFlexContainer.frame.size.width = frame.width - 20
    rootFlexContainer.flex.layout(mode: .adjustHeight)
    if(rootFlexContainer.frame.height != 0) {
        tagsHeight.constant = rootFlexContainer.frame.height
      
    }
    
    
}

虽然这直到我滚动才反映,即重新创建单元格。如何在视图的 TableViewCell 中更新约束?

这是整个 tableviewcell 代码

import UIKit
import FlexLayout
import SDWebImage



class StoreListTableViewCell: UITableViewCell {


@IBOutlet weak var menuLbl: UILabel!


@IBOutlet weak var menuView: UIView!

@IBOutlet weak var cellBgview: UIView!
@IBOutlet weak var storeImg: UIImageView!
@IBOutlet weak var storeLocation: UILabel!
@IBOutlet weak var storeTitle: UILabel!

@IBOutlet weak var cellContainer: UIView!


@IBOutlet weak var stackView: UIStackView!


@IBOutlet weak var tagsHeight: NSLayoutConstraint!
var storeImgStr = ""
var storeTitleStr = ""
var storeLocationStr = ""
var score : Double = 0.0
var tagsStr = ""
var isMenuAvailble = 0
var retailerTags: [String]?
var cashbackString = ""
fileprivate let rootFlexContainer = UIView()



@IBOutlet weak var tagsView: UIView!


@IBOutlet weak var ratingBtn: UIButton!
override func awakeFromNib() {
    super.awakeFromNib()
    self.layoutIfNeeded()
    cellBgview.clipsToBounds = true
    cellBgview.layer.cornerRadius = 5
    cellContainer.layer.shadowColor = UIColor.lightGray.cgColor
    cellContainer.layer.shadowOpacity = 0.5
    cellContainer.layer.shadowRadius = 5.0
    cellContainer.layer.shadowOffset = CGSize(width: 0, height: 2)
    cellContainer.backgroundColor = UIColor.clear
    cellContainer.layer.cornerRadius = 5.0
    cellContainer.layer.borderColor = UIColor.white.cgColor
    cellContainer.layer.borderWidth = 0.5
    ratingBtn.layer.borderWidth = 1
    ratingBtn.layer.cornerRadius = 5
    tagsView.addSubview(rootFlexContainer)
          
    //cellContainer.layer.shadowPath = UIBezierPath(rect: cellBgview.bounds).cgPath
 }

func setSetupStoreUI()  {
    
    if isMenuAvailble == 1 {
        menuView.isHidden = false
        menuView.layer.cornerRadius = 10
    } else {
        menuView.isHidden = true
    }
    
    storeTitle.text = storeTitleStr
   // storeTitle.sizeToFit()
    storeLocation.text = storeLocationStr
    //storeLocation.sizeToFit()
    

    //.filter{ $0.name != " " }
    //storeImg.sd_imageIndicator = SDWebImageActivityIndicator.gray
    storeImg.backgroundColor = UIColor.hexStringToUIColor(hex: AppStrings.placeHolderColor)
    if let url = URL(string: storeImgStr.encoded), !(storeImgStr.isEmpty) {
        self.storeImg.sd_setImage(with: url)
        
    }
   
    
    menuLbl.text = AppLocalString.PREORDER_TEXT.localized()
    menuLbl.font = FontStyle.ProximaNovaBold(size: 12)

    storeTitle.font = FontStyle.ProximaNovaSemibold(size: 14)
    storeLocation.font = FontStyle.ProximaNovaRegular(size: 12)
    storeLocation.textColor = .gray
  //        ratingBtn.isHidden = (score == 0)
    ratingBtn.setTitle(score == 0 ? "-" : String(format: "%.1f", score), for: .normal)
    ratingBtn.backgroundColor = UIColor.hexStringToUIColor(hex: Utility.getRatingColor(rating: score))
    ratingBtn.layer.borderColor = UIColor.hexStringToUIColor(hex: Utility.getRatingColor(rating: score)).cgColor
   
    rootFlexContainer.subviews.forEach({ $0.removeFromSuperview() })
    //tagsView.willRemoveSubview(rootFlexContainer)
    //rootFlexContainer.frame = tagsView.frame
     //tagsView.addSubview(rootFlexContainer)
    rootFlexContainer.flex.direction(.row).wrap(.wrap).alignSelf(.auto).justifyContent(.start).paddingRight(2).define { (flex) in
        for i in 0..<((retailerTags?.count ?? 0) > 3 ? 3 : (retailerTags?.count ?? 0)) {
            let nameLabel = UIButton()
            nameLabel.isUserInteractionEnabled = false
            nameLabel.setTitle((retailerTags?[i] ?? "").trim(), for: .normal)
            nameLabel.setTitleColor(.black, for: .normal)
            nameLabel.titleLabel?.font = FontStyle.ProximaNovaRegular(size: 11)
            nameLabel.contentEdgeInsets = UIEdgeInsets(top: 1.5, left: 4, bottom: 1.5, right:4)
            nameLabel.layer.borderColor = UIColor.hexStringToUIColor(hex: AppStrings.grayBorderColor).cgColor
            nameLabel.layer.cornerRadius = 8
            nameLabel.layer.borderWidth = 1.0
            nameLabel.sizeToFit()
            flex.addItem(nameLabel).margin(2)
            
        }
        if cashbackString != "" {
        let cashbackLabel = UIButton()
            
            cashbackLabel.backgroundColor = UIColor.hexStringToUIColor(hex: AppStrings.orangeCashbackColor)
            cashbackLabel.isUserInteractionEnabled = false
            cashbackLabel.setTitle(cashbackString, for: .normal)
            cashbackLabel.setTitleColor(.black, for: .normal)
            cashbackLabel.titleLabel?.font = FontStyle.ProximaNovaRegular(size: 10)
            cashbackLabel.contentEdgeInsets = UIEdgeInsets(top: 1.5, left: 5, bottom: 1.5, right: 5)
            cashbackLabel.layer.cornerRadius = 5
            cashbackLabel.layer.borderWidth = 0
            cashbackLabel.sizeToFit()
            flex.addItem(cashbackLabel).margin(2)
        }
          
    }
    rootFlexContainer.flex.layout()
    if retailerTags?.count ?? 0 == 0 {
        tagsView.isHidden = true
    } else {
        tagsView.isHidden = false
    }
    
   
    
}


override func layoutSubviews() {
    super.layoutSubviews()
    layout()
}

fileprivate func layout() {
    rootFlexContainer.frame.size.width = frame.width - 20
    rootFlexContainer.flex.layout(mode: .adjustHeight)
    if(rootFlexContainer.frame.height != 0) {
        tagsHeight.constant = rootFlexContainer.frame.height
      
    }
    
    
}



override func setSelected(_ selected: Bool, animated: Bool) {
    super.setSelected(selected, animated: animated)
    
    // Configure the view for the selected state
}

}

以下是故事板中的视图层次结构

【问题讨论】:

  • 我遇到过这个问题,约束不是表格视图单元内的自动更新。我从 Viewcontroller 类解决了这个问题。在视图控制器中,我计算基于表格单元高度的元数据并将高度返回给表格单元高度方法。您需要从表格单元类中删除 layoutSubviews、setSetupStoreUI 方法,它应该在视图控制器类中计算,并使用在视图控制器类中声明的变量返回高度。

标签: ios uitableview constraints tableview


【解决方案1】:

一些观察 -

1.不必要的layoutIfNeeded() 内部调用awakeFromNib()

override func awakeFromNib() {
    super.awakeFromNib()

    /*
    self.layoutIfNeeded() // Not Necessary, Remove it.
    */
    

    // No other changes to current implementation
}

2。以下不应该是必要的,删除它。

/* Not necessary, Remove it.
override func layoutSubviews() {
    super.layoutSubviews()
    layout()
}

fileprivate func layout() {
    rootFlexContainer.frame.size.width = frame.width - 20
    rootFlexContainer.flex.layout(mode: .adjustHeight)
    if(rootFlexContainer.frame.height != 0) {
        tagsHeight.constant = rootFlexContainer.frame.height
    }
}
*/

3.在填充所有详细信息的方法中,进行以下更改

func setSetupStoreUI()  {
    // No changes to current implementation

    /* Remove this, we will update later
    rootFlexContainer.flex.layout()
    */

    if retailerTags?.count ?? 0 == 0 {
        tagsView.isHidden = true
    } else {
        tagsView.isHidden = false
    }
    
    // Copied as is from `layout()` 
    rootFlexContainer.frame.size.width = frame.width - 20
    rootFlexContainer.flex.layout(mode: .adjustHeight)
    if (rootFlexContainer.frame.height != 0) {
        tagsHeight.constant = rootFlexContainer.frame.height
    }

    // do a manual layout (on contentView, NOT self)
    self.contentView.layoutIfNeeded()
}

【讨论】:

  • 嗨,这个仍然无法正常工作的 flex 布局会导致问题。底部的芯片水平超出界限,但滚动后它们看起来很好
  • 期望是您从cellForRow(at: indexPath) 方法内部调用setSetupStoreUI()。在UITableView 的情况下,所有单元格都使用它的全宽,因此在分配宽度后调用flex.layout(mode: .adjustHeight) 应该做正确的事情。您可以检查frame.width 第一次调用时(发生问题的地方)和第二次调用时(正常工作的地方)的值是多少,并调查宽度值存在差异的原因(如果存在)。跨度>
  • 谢谢,问题在于你的宽度是对的
【解决方案2】:

您必须告诉表格视图控制器您的单元格高度已更改。

最常见的方法是协议/委托模式或(swift 首选)闭包。

例如...

在你的单元格中:

class MyTableViewCell: UITableViewCell {
    
    // "callback" closure
    var cellHeightChanged: (()->())?
    
    // whatever happens that you need to change the cell height
    func heightChanged() -> Void {
        tagsHeight.constant = rootFlexContainer.frame.height
        cellHeightChanged?()
    }

}

然后,在您的控制器中,cellForRowAt

cell.cellHeightChanged = {
    tableView.performBatchUpdates(nil, completion: nil)
}

【讨论】:

  • 得到 exc_bad_access 异常
  • @amodkanthe - 您需要提供更多信息。你从哪里得到错误?您是否正确编辑了您的课程?你设置断点调试了吗?
  • @amodkanthe - 我们必须查看您的完整代码,而不仅仅是 sn-p。参考minimal reproducible example