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:
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:
Related Topics
Check the Position of the Xcuielement on Screen While Testing iOS Application Using Xctest
How to Reload Data Properly When Retrieving Objects from Parse
Pass Extra Argument for UItapgesturerecognizer with Selector
How to Make a Countdown Timer Like in a Music Player
How to Detect If The HTML5 Autoplay Attribute Is Supported
Passing Arguments to @Selector Method
In What Format Is This Date String
Passing Data Between View Controllers: from UItableview to a Details View Controller
How to Print Name of The Day of The Week
How to Request Photo Gallery Permissions in iOS a Second Time
How to Remove "Optional" from String
Using Delegates to Transfer Data from One Tableview to Another
Swift Compiler Error: Use of Unresolved Identifier 'Name'
How to Add a Lock Screen Widget (Requiring iOS 16) and Still Support iOS 15
How to Switch Programmatically to Dark Mode Swift
How to Loop Through and Get All The Keys of The Nested Nodes in Firebase