Uitableview Automatic Dimension Not Working Correctly

why UITableViewAutomaticDimension not working?

In order to make UITableViewAutomaticDimension work you have to set all left, right, bottom, and top constraints relative to cell container view. In your case you will need to add the missing bottom space to superview constraint for label under the title

UITableViewAutomaticDimension not working with custom table cell

Automatic height works on calculated cell's contentView height. You therefore have to add constraints that will be used to calculate its real height.

Try this:

class UINoteTabelViewCell: UITableViewCell {
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
self.selectionStyle = .none
}

func fill(_ note: Note) {
let view = UINote(withNote: note, atPoint: .zero)
self.contentView.addSubview(view)

// this will make the difference to calculate it based on view's size:
view.translatesAutoresizingMaskIntoConstraints = false
view.leftAnchor.constraint(equalTo: contentView.leftAnchor).isActive = true
view.rightAnchor.constraint(equalTo: contentView.rightAnchor).isActive = true
view.topAnchor.constraint(equalTo: contentView.topAnchor).isActive = true
view.bottomAnchor.constraint(equalTo: contentView.bottomAnchor).isActive = true
}
}

Also, estimatedRowHeight should be set to a specific value that approximately estimates the size of all cells. But that would not be a problem.

UITableViewAutomaticDimension works not as expected. Swift

You're doing a number of things wrong, but the main point is your use of greaterThanOrEqualTo:.

Instead, it should be:

cellCaption.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -8),

Also, your current code is adding a new label as a subview every time you set the text. Cells are reused, so you only want to add the label when the cell is created.

Next, the correct properties for the table are:

    tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 44

Put those two lines in viewDidLoad() of your table view controller, and do not implement heightForRowAt or estimatedHeightForRowAt functions. You can delete your extension entirely.

And finally, you only need to set the constraints once. Definitely NOT in layoutSubviews().

Here's a full example:

//
// NotesTableViewController.swift
//
// Created by Don Mag on 8/29/18.
//

import UIKit

class NotesModel: NSObject {
var name: String = ""
}

class NotesCell: UITableViewCell {
lazy private var cellCaption: UILabel = {
let label = UILabel()

label.translatesAutoresizingMaskIntoConstraints = false
label.font = UIFont.systemFont(ofSize: 20, weight: UIFont.Weight.medium)
label.numberOfLines = 0
label.lineBreakMode = .byWordWrapping

return label
}()

func configure(with note: NotesModel) {
cellCaption.text = note.name
}

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

required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}

func commonInit() -> Void {

contentView.addSubview(cellCaption)

NSLayoutConstraint.activate([
cellCaption.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 8),
cellCaption.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 8),
cellCaption.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -8),
cellCaption.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -8),
])

}

}

class NotesTableViewController: UITableViewController {

override func viewDidLoad() {
super.viewDidLoad()

tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 44
}

// MARK: - Table view data source

override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 8
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "NotesCell", for: indexPath) as! NotesCell

let m = NotesModel()

if indexPath.row == 3 {
m.name = "This is a very long caption. It will demonstrate how the cell height is auto-sized when the text is long enough to wrap to multiple lines."
} else {
m.name = "Caption \(indexPath.row)"
}

cell.configure(with: m)

return cell
}

}

Result:

Sample Image

UITableViewAutomaticDimension not working on iOS 8

add following code in "cellForRowAtIndexPath" method before return cell.

    cell.setNeedsUpdateConstraints()
cell.updateConstraintsIfNeeded()

UITableViewAutomaticDimension doesn't seem to take the detail label's content into account. How can I fix it?

It's going to be difficult to get this to work exactly as you want.

Auto-layout makes multiple "passes" to try and satisfy layout requirements. However, think about what it has to do:

  • Set the text of both labels
  • Does "leftLabel" need to wrap?
  • Does "rightLabel" need to wrap?
  • If "leftLabel" wraps, does "rightLabel" still need to wrap?
  • and vice-versa?

And, at what point in that process could it be determined that both labels will end up wrapping, so make each one 50% of the width? Keeping in mind that the cell width will vary, depending on device and orientation?

You may want to look at the actual data you expect to be displaying, and think about how it will actually look. A "right detail cell" design may not end up as the best layout.

That said - here is a custom cell that might work for you. It will need plenty of edge-testing to see if any sizing gets thrown off (for example, by very long strings and/or by very big differentials between string lengths):

class MyRightDetailCell: UITableViewCell {

var myTextLabel: UILabel = {
let v = UILabel()
v.translatesAutoresizingMaskIntoConstraints = false
v.numberOfLines = 0
v.textAlignment = .left
v.font = UIFont.systemFont(ofSize: 17.0)
v.textColor = .black
v.setContentHuggingPriority(.required, for: .horizontal)
v.setContentHuggingPriority(.required, for: .vertical)
v.setContentCompressionResistancePriority(.required, for: .horizontal)
v.setContentCompressionResistancePriority(.required, for: .vertical)
return v
}()

var myDetailTextLabel: UILabel = {
let v = UILabel()
v.translatesAutoresizingMaskIntoConstraints = false
v.numberOfLines = 0
v.textAlignment = .right
v.font = UIFont.systemFont(ofSize: 17.0)
v.textColor = .darkGray
v.setContentHuggingPriority(.required, for: .horizontal)
v.setContentHuggingPriority(.required, for: .vertical)
v.setContentCompressionResistancePriority(.required, for: .horizontal)
v.setContentCompressionResistancePriority(.required, for: .vertical)
return v
}()

var theStackView: UIStackView = {
let v = UIStackView()
v.translatesAutoresizingMaskIntoConstraints = false
v.axis = .horizontal
v.alignment = .top
v.distribution = .fill
v.spacing = 8
v.setContentHuggingPriority(.required, for: .vertical)
v.setContentCompressionResistancePriority(.required, for: .vertical)
return v
}()

override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
commonInit()
}

required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}

func commonInit() -> Void {

contentView.addSubview(theStackView)
theStackView.addArrangedSubview(myTextLabel)
theStackView.addArrangedSubview(myDetailTextLabel)

let g = contentView.layoutMarginsGuide

NSLayoutConstraint.activate([
theStackView.topAnchor.constraint(equalTo: g.topAnchor, constant: 0.0),
theStackView.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: 0.0),
theStackView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 0.0),
theStackView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: 0.0),
])

}

}

class RightDetailTableViewController: UITableViewController {

var strings = [
("Some Very Long Black Text That Doesn't Fit. Foo Bar Baz.", ""),
("A", "Short Text"),
("B Somewhat Much Longer Left Label Text.", "With long right label text."),
("Working Now?", "Some Very Long Grey Text (Three Lines?) That Doesn't Fit. Foo Bar Baz"),
("C", "Another Text"),
("D", "With Long Right Label Text That Will Need To Wrap."),
]

let cellID = "MYRDC"

override func viewDidLoad() {
super.viewDidLoad()

tableView.register(MyRightDetailCell.self, forCellReuseIdentifier: cellID)

// make 5 copies of the test data so we'll have plenty of rows
// for scrolling (to check cell re-use)
for _ in 1...5 {
strings.append(contentsOf: strings)
}
}

override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)

// due to the complexity of the cell, the layout is more reliable if the
// table is reloaded here - try it with and without this extra reloadData() call
tableView.reloadData()
}

// MARK: - Table view data source

override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return strings.count
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellID, for: indexPath) as! MyRightDetailCell

cell.myTextLabel.text = strings[indexPath.row].0
cell.myDetailTextLabel.text = strings[indexPath.row].1

// if either label has no text (""), set it to hidden
// to remove the stack view's spacing
cell.myTextLabel.isHidden = (strings[indexPath.row].0 == "")
cell.myDetailTextLabel.isHidden = (strings[indexPath.row].1 == "")

// un-comment the next two lines to show label background colors
// to make it easy to see the label frames
//cell.myTextLabel.backgroundColor = .cyan
//cell.myDetailTextLabel.backgroundColor = .green

return cell
}

}

Output:

Sample Image



Related Topics



Leave a reply



Submit