如何在表视图中的UITableViewCells中使用“自动布局”,让每个单元格的内容和子视图确定行高度(自身/自动),同时保持平滑的滚动性能?


当前回答

在我的例子中,填充是因为sectionHeader和sectionFooter的高度,故事板允许我将其更改为最小值1。因此,在viewDidLoad方法中:

tableView.sectionHeaderHeight = 0
tableView.sectionFooterHeight = 0

其他回答

如果单元格高度根据内容是动态的,则应精确计算出它,然后在渲染单元格之前返回高度值。一种简单的方法是在表视图单元格代码中定义计数方法,以便控制器在表单元格高度委托方法处调用。如果高度取决于表格或屏幕的宽度,请不要忘记计算实际单元格框架宽度(默认值为320)。也就是说,在表单元格高度委托方法中,首先使用cell.frame来校正单元格宽度,然后调用单元格中定义的计数高度方法来获得合适的值并返回它。

PS。可以在另一个方法中定义用于生成单元格对象的代码,以便调用不同的表视图单元格委托方法。

如果您以编程方式进行布局,以下是在Swift中使用锚的iOS 10需要考虑的问题。

有三个规则/步骤

数字1:在viewDidLoad上设置tableview的这两个财产,第一个属性告诉表视图应该期望其单元格的动态大小,第二个属性只是让应用程序计算滚动条指示器的大小,这样有助于提高性能。

    tableView.rowHeight = UITableViewAutomaticDimension
    tableView.estimatedRowHeight = 100

第二:这一点很重要。您需要将子视图添加到单元格的contentView中,而不是添加到视图中,还需要使用其layoutsmargingguide将子视图锚定到顶部和底部,这是一个很好的示例。

override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
    super.init(style: style, reuseIdentifier: reuseIdentifier)
    setUpViews()
}

private func setUpViews() {

    contentView.addSubview(movieImageView)
    contentView.addSubview(descriptionLabel)
    let marginGuide = contentView.layoutMarginsGuide

    NSLayoutConstraint.activate([
        movieImageView.heightAnchor.constraint(equalToConstant: 80),
        movieImageView.widthAnchor.constraint(equalToConstant: 80),
        movieImageView.leftAnchor.constraint(equalTo: marginGuide.leftAnchor),
        movieImageView.topAnchor.constraint(equalTo: marginGuide.topAnchor, constant: 20),

        descriptionLabel.leftAnchor.constraint(equalTo: movieImageView.rightAnchor, constant: 15),
        descriptionLabel.rightAnchor.constraint(equalTo: marginGuide.rightAnchor),
        descriptionLabel.bottomAnchor.constraint(equalTo: marginGuide.bottomAnchor, constant: -15),
        descriptionLabel.topAnchor.constraint(equalTo: movieImageView.topAnchor)

        ])
}

创建一个将添加子视图并执行布局的方法,在init方法中调用它。

第3:不要调用方法:

  override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    }

如果你这样做,你将覆盖你的实现。

对于表视图中的动态单元格,请遵循以下3条规则。

这是一个有效的实现https://github.com/jamesrochabrun/MinimalViewController

对于上面的iOS 8,这非常简单:

override func viewDidLoad() {  
    super.viewDidLoad()

    self.tableView.estimatedRowHeight = 80
    self.tableView.rowHeight = UITableView.automaticDimension
}

or

func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
    return UITableView.automaticDimension
}

但对于iOS 7,关键是计算自动布局后的高度:

func calculateHeightForConfiguredSizingCell(cell: GSTableViewCell) -> CGFloat {
    cell.setNeedsLayout()
    cell.layoutIfNeeded()
    let height = cell.contentView.systemLayoutSizeFittingSize(UILayoutFittingExpandedSize).height + 1.0
    return height
}

重要的

如果有多行标签,请不要忘记将numberOfLines设置为0。不要忘记label.preferredMaxLayoutWidth=CGRectGetWidth(tableView.bounds)

完整的示例代码在这里。

swift 4

    @IBOutlet weak var tableViewHeightConstraint: NSLayoutConstraint!
    @IBOutlet weak var tableView: UITableView!
    private var context = 1
 override func viewDidLoad() {
        super.viewDidLoad()

        self.tableView.addObserver(self, forKeyPath: "contentSize", options: [.new,.prior], context: &context)
    }
  // Added observer to adjust tableview height based on the content

    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        if context == &self.context{
            if let size = change?[NSKeyValueChangeKey.newKey] as? CGSize{
                print("-----")
                print(size.height)
                tableViewHeightConstraint.constant = size.height + 50
            }
        }
    }

//Remove observer
 deinit {

        NotificationCenter.default.removeObserver(self)

    }

Swift中的另一个iOs7+iOs8解决方案

var cell2height:CGFloat=44

override func viewDidLoad() {
    super.viewDidLoad()
    theTable.rowHeight = UITableViewAutomaticDimension
    theTable.estimatedRowHeight = 44.0;
}

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell =  tableView.dequeueReusableCellWithIdentifier("myTableViewCell", forIndexPath: indexPath) as! myTableViewCell
    cell2height=cell.contentView.height
    return cell
}

func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
    if #available(iOS 8.0, *) {
        return UITableViewAutomaticDimension
    } else {
        return cell2height
    }
}