Dynamic UITableView row height using UIStackView?
It seems that for this to work the constraints need to be added in the init of the UITableViewCell
and added to the contentView
instead of cell's view.
The working code looks like this:
import UIKit
class StackCell : UITableViewCell {
enum VisualFormat: String {
case HorizontalStackViewFormat = "H:|[stackView]|"
case VerticalStackViewFormat = "V:|[stackView(>=44)]|"
}
var hasSetupConstraints = false
lazy var stackView : UIStackView! = {
let stack = UIStackView()
stack.axis = UILayoutConstraintAxis.Vertical
stack.distribution = .FillProportionally
stack.alignment = .Fill
stack.spacing = 3.0
stack.translatesAutoresizingMaskIntoConstraints = false
stack.setContentCompressionResistancePriority(UILayoutPriorityRequired, forAxis: .Vertical)
return stack
}()
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
contentView.addSubview(stackView)
addStackConstraints()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func addStackConstraints() {
let viewsDictionary: [String:AnyObject] = ["stackView" : stackView]
var newConstraints = [NSLayoutConstraint]()
newConstraints += self.newConstraints(VisualFormat.HorizontalStackViewFormat.rawValue, viewsDictionary: viewsDictionary)
newConstraints += self.newConstraints(VisualFormat.VerticalStackViewFormat.rawValue, viewsDictionary: viewsDictionary)
contentView.addConstraints(newConstraints)
super.updateConstraints()
}
private func newConstraints(visualFormat: String, viewsDictionary: [String:AnyObject]) -> [NSLayoutConstraint] {
return NSLayoutConstraint.constraintsWithVisualFormat(visualFormat, options: [], metrics: nil, views: viewsDictionary)
}
}
![memmons](https://i.stack.imgur.com/LFf3z.jpg?s=256&g=1)
memmons
Founder of App Apps, LLC. Creator of Audiotorium Notes for iPad & VideoBot.
Updated on June 19, 2022Comments
-
memmons about 2 years
Surprised this isn't working out of the box, as this seems to be an important use case for stack views. I have a
UITableViewCell
subclass which adds a UIStackView to the contentView. I'm adding labels to the stack view intableView(_cellForRowAtIndexPath:)
and the tableview is set to use dynamic row heights, but it doesn't appear to work, at least in Xcode 7.3. I was also under the impression that hiding arranged subviews in a stack view was animatable, but that seems broken as well.Any ideas on how to get this working correctly?
class StackCell : UITableViewCell { enum VisualFormat: String { case HorizontalStackViewFormat = "H:|[stackView]|" case VerticalStackViewFormat = "V:|[stackView(>=44)]|" } var hasSetupConstraints = false lazy var stackView : UIStackView! = { let stack = UIStackView() stack.axis = .Vertical stack.distribution = .FillProportionally stack.alignment = .Fill stack.spacing = 3.0 stack.translatesAutoresizingMaskIntoConstraints = false return stack }() override init(style: UITableViewCellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) contentView.addSubview(stackView) } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func updateConstraints() { if !hasSetupConstraints { hasSetupConstraints = true let viewsDictionary: [String:AnyObject] = ["stackView" : stackView] var newConstraints = [NSLayoutConstraint]() newConstraints += self.newConstraints(VisualFormat.HorizontalStackViewFormat.rawValue, viewsDictionary: viewsDictionary) newConstraints += self.newConstraints(VisualFormat.VerticalStackViewFormat.rawValue, viewsDictionary: viewsDictionary) addConstraints(newConstraints) } super.updateConstraints() } private func newConstraints(visualFormat: String, viewsDictionary: [String:AnyObject]) -> [NSLayoutConstraint] { return NSLayoutConstraint.constraintsWithVisualFormat(visualFormat, options: [], metrics: nil, views: viewsDictionary) } class ViewController: UITableViewController { private let reuseIdentifier = "StackCell" private let cellClass = StackCell.self override func viewDidLoad() { super.viewDidLoad() configureTableView(self.tableView) } private func configureTableView(tableView: UITableView) { tableView.registerClass(cellClass, forCellReuseIdentifier: reuseIdentifier) tableView.separatorStyle = .SingleLine tableView.estimatedRowHeight = 88 tableView.rowHeight = UITableViewAutomaticDimension } private func newLabel(title: String) -> UILabel { let label = UILabel() label.text = title return label } // MARK: - UITableView override func numberOfSectionsInTableView(tableView: UITableView) -> Int { return 4 } override func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { return 44.0 } override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return 10 } override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCellWithIdentifier(reuseIdentifier, forIndexPath: indexPath) as! StackCell cell.stackView.arrangedSubviews.forEach({$0.removeFromSuperview()}) cell.stackView.addArrangedSubview(newLabel("\(indexPath.section)-\(indexPath.row)")) cell.stackView.addArrangedSubview(newLabel("Second Label")) cell.stackView.addArrangedSubview(newLabel("Third Label")) cell.stackView.addArrangedSubview(newLabel("Fourth Label")) cell.stackView.addArrangedSubview(newLabel("Fifth Label")) return cell } override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) { let cell = tableView.cellForRowAtIndexPath(indexPath) as! StackCell for (idx, view) in cell.stackView.arrangedSubviews.enumerate() { if idx == 0 { continue } view.hidden = !view.hidden } UIView.animateWithDuration(0.3, animations: { cell.contentView.layoutIfNeeded() tableView.beginUpdates() tableView.endUpdates() }) } }