3

After reading Ray Wenderlich guide for "Self-sizing Table View Cells" as well as this question and answers to it, I've decided to ask all of you for a help.

Have a programmically created cell:

import UIKit

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

    contentView.addSubview(cellCaption)
}

override func layoutSubviews() {
    super.layoutSubviews()

    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),
cellCaption.bottomAnchor.constraint(greaterThanOrEqualTo: contentView.bottomAnchor, constant: -8)
            ])

//        cellCaption.sizeToFit()
//        cellCaption.layoutIfNeeded()
}
}

The table view controller uses UITableViewAutomaticDimension in the delegate methods:

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

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

}

As a result, the longest caption is indicated fully, but the cell anyway has the same height as all other.

enter image description here

Some update!

I've already tried to put into viewDidLoad() following code:

tableView.rowHeight = 44
tableView.estimatedRowHeight = UITableViewAutomaticDimension

with enabling delegate methods and disabling them as well. The result is the same :(

Yuri Ivashin
  • 55
  • 1
  • 7

5 Answers5

6

I would recommend not to use the Delegate-Methods for your needs.

Just try setting this in your viewDidLoad:

self.tableView.rowHeight = UITableViewAutomaticDimension;
// set estimatedRowHeight to whatever is the fallBack rowHeight
self.tableView.estimatedRowHeight = 44.0;

This always works for me. Let me know if it helps :)

Teetz
  • 3,475
  • 3
  • 21
  • 34
  • @YuriIvashin if this doesn't work you should have a deeper look into your constraints. The cell needs to be able to calculate the height of your cell without a doubt. – Teetz Aug 29 '18 at 09:34
  • Constraints are easy as it possible. Tried to change priority for top an bottom constraint. Result is the same – Yuri Ivashin Aug 29 '18 at 09:42
  • @YuriIvashin Did you have a look here? https://stackoverflow.com/questions/27288507/change-nslayoutconstraint-constant-not-working-in-layoutsubviews – Teetz Aug 29 '18 at 09:50
  • Have put into tableViewController: override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() tableView.layoutSubviews() } as well as calling self.layoutIfNeeded() inside the cell's layoutSubviews. Result is the same – Yuri Ivashin Aug 29 '18 at 09:59
  • If you want me to help further show it in your question. Sounds like you put it in TableViewController but you should put it in your TableViewCell i guess. – Teetz Aug 29 '18 at 10:29
  • I do appreciate your help. But take into account what whis is a Table view Cell. Value of type 'UITableViewCell' has no member 'viewDidLayoutSubviews' – Yuri Ivashin Aug 29 '18 at 11:11
  • @YuriIvashin Oh sorry. You are right. Anyway: Did you try to place your code from layoutSubviews in configure after you add the subview? (I think layoutSubviews is triggered before you add the subview itself) – Teetz Aug 29 '18 at 11:39
  • I've put NSLayoutConstraint.activate and self.layoutIfNeeded() into he configure function with fully commented layoutSubviews(). Result is the same – Yuri Ivashin Aug 29 '18 at 12:18
5

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:

enter image description here

DonMag
  • 69,424
  • 5
  • 50
  • 86
0

Update the following line:

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

to:

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

and add following in viewDidLoad:

self.tableView.rowHeight = UITableViewAutomaticDimension;
self.tableView.estimatedRowHeight = 44.0;
Atif Khan
  • 301
  • 1
  • 2
  • 11
0

Following steps may solve your problem:

1) set top, bottom, leading and trailing constraints for the UILabel in the cell like below: enter image description here

2) configure tableview:

self.tableView.rowHeight = UITableViewAutomaticDimension;
self.tableView.estimatedRowHeight = 44.0;
Dogan Altinbas
  • 268
  • 2
  • 11
0

In Swift 5:

func configureTableView() {
        myTableView.rowHeight =  UITableView.automaticDimension
        myTableView.estimatedRowHeight = 44
    }

Keep in mind that if the .estimatedRowHeight is not correct, Swift will do the math for you. Finally, call this method in the viewDidLoad()

Victor Ruiz.
  • 1,706
  • 22
  • 22