代码之家  ›  专栏  ›  技术社区  ›  Marcin Kapusta

使用基于行内容的autolayout动态计算UITableViewCell高度

  •  0
  • Marcin Kapusta  · 技术社区  · 7 年前

    从上到下都准备好了nibuit文件中的所有约束。在表视图中计算行高似乎是可行的。

    但是,如果我想根据某些行数据条件激活/取消激活单元格中的其他约束,那么我就有问题了,因为似乎那时没有计算高度。有什么问题吗?在下面的示例中 TableRowDM.hidden 不应该隐藏字幕的意思。我隐藏这个UILabel的方法是激活not activated约束(height with constant=0),并将titleLabel和subtitleLabel之间的垂直间距设置为0。

    struct TableRowDM {
        let title: String
        let subTitle: String
        let hidden: Bool
    }
    
    let cellId: String = "CellView"
    
    class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
    
        @IBOutlet weak var tableView: UITableView!
    
        let rowData: [TableRowDM] = [
            TableRowDM(title: "Row 1", subTitle: "Short title 1th row", hidden: false),
            TableRowDM(title: "Row 2", subTitle: "Short title 2th row", hidden: true),
            TableRowDM(title: "Row 3", subTitle: "Very long text in subtitle at 3th row to test text wrapping and growing subtitle height", hidden: false),
            TableRowDM(title: "Row 4", subTitle: "Long text in subtitle at 4th row", hidden: false),
            TableRowDM(title: "Row 5", subTitle: "Long text in subtitle at 5th row", hidden: true),
            TableRowDM(title: "Row 6", subTitle: "Long text in subtitle at 6th row", hidden: false),
        ]
    
        override func viewDidLoad() {
            super.viewDidLoad()
            tableView.rowHeight = UITableViewAutomaticDimension
            tableView.estimatedRowHeight = 50
            tableView.tableFooterView = UIView(frame: CGRect.zero)
            tableView.register(UINib(nibName: cellId, bundle: nil), forCellReuseIdentifier: cellId)
            tableView.delegate = self
            tableView.dataSource = self
        }
    
        func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath) as! CellView
            let row: TableRowDM = self.rowData[indexPath.row]
    
            cell.title.text = row.title
            cell.subtitle.text = row.subTitle
    
            if row.hidden {
                // hide subtitle
                cell.subtitleHeight.isActive = true
                cell.titleToSubtitleGap.constant = 0
            } else {
                // show subtitle
                cell.subtitleHeight.isActive = false
                cell.titleToSubtitleGap.constant = 16
            }
            cell.setNeedsLayout()
            cell.layoutIfNeeded()
            return cell
        }
        func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
            return rowData.count
        }
    }
    

    -----------------
          |
    - titleLabel ----
          |
          | <- titleToSubtitleGap
          |
    - subtitleLabel -
          |
    -----------------
    
    1 回复  |  直到 7 年前
        1
  •  0
  •   Marcin Kapusta    7 年前

    我找到了解决问题的办法。它没有记录在任何地方,但它是有效的。

    什么是重要的。如果您支持横向/纵向方向,那么在NSLayoutConstraints中设置正确的优先级是很重要的,它定义了垂直的连续约束链和视图(具有定义的高度) here -他们写道:

    查看。要定义单元高度,需要一个完整的 用于填充之间区域的约束和视图(具有定义的高度) 内容查看上边缘和下边缘。如果你的观点 必须向视图或 内容视图本身。

    所有那些最终定义表单元格高度的垂直约束 contentView

    updateConstraints 方法。所以,如果你正在设计一个UIView,当这个视图的一些属性需要改变约束时,你需要在你的视图中这样做 更新约束 方法将在适当的时候被调用。设置属性新值调用后 needsUpdateConstraints()

    import UIKit
    
    class ListItemTableViewCell: UITableViewCell {
    
        @IBOutlet weak var subtitleToTitleGap: NSLayoutConstraint!
        @IBOutlet weak var subtitleHeightZero: NSLayoutConstraint!
    
        @IBOutlet weak var titleLabel: UILabel!
        @IBOutlet weak var subtitleLabel: UILabel!
    
        public var isSubtitleHidden: Bool {
            get {
                return self.subtitleLabel.isHidden
            }
            set {
                // Here we are changing property that change layout in our view
                self.subtitleLabel.isHidden = newValue
                self.needsUpdateConstraints() // Here we are telling AutoLayout to recalculate constraints in `updateConstraints` method.
            }
        }
    
    #if !TARGET_INTERFACE_BUILDER
        // We need to block this code from running in Interface Builder
        override func updateConstraints() {
            if self.isSubtitleHidden {
                // Hide Subtitle
                self.subtitleToTitleGap.constant = 0.0
                self.subtitleHeightZero.autoInstall() // This makes subtitle height to be 0
            } else {
                // Show Subtitle
                self.subtitleToTitleGap.constant = 2.0
                self.subtitleHeightZero.autoRemove() // This will make subtitle height to be self calculated
            }
            super.updateConstraints() // don't forget to call this after You finished constraints changes
        }
    #endif
    }
    

    而最重要也是最难发现的就是隐藏实施 #if