0

I have a custom subclass of UITableViewController. It has one section containing many rows. Each row corresponds to the same custom table view cell class. Each custom cell has two labels: myLabel1 & myLabel2, both subviews of the cell's contentView.

Every myLabel1 has one line of text, and every myLabel2 has one or two lines of text, but every cell should have the same height, as if every myLabel2 has two lines of text.

The labels use Dynamic Type.

myLabel1.font = UIFont.preferredFont(forTextStyle: .headline)
myLabel2.font = UIFont.preferredFont(forTextStyle: .subheadline)

According to Working with Self-Sizing Table View Cells, I've positioned each label with Auto Layout and "set the table view’s rowHeight property to UITableViewAutomaticDimension" so that the row height changes with Dynamic Type.

  1. How do I make every cell have the same height?

  2. How should I estimate the table view's row height?

ma11hew28
  • 106,283
  • 107
  • 420
  • 616
  • It depends on the content of cells. With self-sizing u cant control the size. You have to set the same content. Or dont use self-sizing if u want same size. – Muhammad Zohaib Ehsan Dec 28 '16 at 06:10
  • Create a constraint that makes the height of label2 equal to label1 . – AppreciateIt Dec 28 '16 at 06:15
  • Basically every cell of the `UITableView` should have the maximum height that any one cell of your `UITableView` may have at run time? – Rikh Dec 28 '16 at 06:15
  • remove UITableViewAutomaticDimension for fix sizing. and use tableView delegate method to specify fix or calculated row height . – Muhammad Adnan Dec 28 '16 at 06:19
  • I want every cell to have the same height, and that height should change depending on each user's [Dynamic Type](https://developer.apple.com/ios/human-interface-guidelines/visual-design/typography/) settings. "Dynamic Type provides additional flexibility by letting readers choose their preferred text size." – ma11hew28 Dec 28 '16 at 06:23
  • http://stackoverflow.com/questions/25180443/adjust-uilabel-height-to-text calculate height for both labels for your maximum possible text. Set it as height for cell in tableview Delegate method . – Muhammad Adnan Dec 28 '16 at 06:55
  • Are you sure that you want every single cell to be the same height? That's counter to how self-sizing table cells work, where the height can be different per cell. – Ben Dec 29 '16 at 22:37

2 Answers2

2

UITableViewAutomaticDimension will estimate the cell size depending on cells content size. We must calculate the max height that a cell could possibly have, and return this height for all cells instead of using UITableViewAutomaticDimension.

CustomTableViewCell

let kMargin: CGFloat = 8.0

class CustomTableViewCell: UITableViewCell {

    @IBOutlet var singleLineLabel: UILabel!
    @IBOutlet var doubleLineLabel: UILabel!

    override func awakeFromNib() {
        super.awakeFromNib()
        // Initialization code
        updateFonts()
    }

    override func prepareForReuse() {
        updateFonts()
    }

    func updateFonts() {
        singleLineLabel.font = UIFont.preferredFont(forTextStyle:.title3)
        doubleLineLabel.font = UIFont.preferredFont(forTextStyle:.body)
    }

    func updateCellForCellWidth(_ width:CGFloat) {
        doubleLineLabel.preferredMaxLayoutWidth = width - (2*kMargin)
    }

    func fillCellWith(_ firstString: String, _ secondString: String) {
        singleLineLabel.text = firstString
        doubleLineLabel.text = secondString
    }
}

On View Controller

Setting up a dummy cell and listing to notifications for dynamic type

var heightCalculatorDummyCell: CustomTableViewCell!
    var maxHeight: CGFloat = 0.0

    override func viewDidLoad() {
        super.viewDidLoad()
        heightCalculatorDummyCell = tableView.dequeueReusableCell(withIdentifier: "cell_id") as! CustomTableViewCell
        maxHeight = getMaxHeight()

        NotificationCenter.default.addObserver(self, selector: #selector(AutomaticHeightTableViewController.didChangePreferredContentSize), name: .UIContentSizeCategoryDidChange, object:nil)
    }

Getting max height using a dummy cell.

func getMaxHeight() -> CGFloat {
        heightCalculatorDummyCell.updateFonts()
        heightCalculatorDummyCell.fillCellWith("Title","A string that needs more than two lines. A string that needs more than two lines. A string that needs more than two lines. A string that needs more than two lines. A string that needs more than two lines.")
        heightCalculatorDummyCell.updateCellForCellWidth(tableView.frame.size.width)
        let size = heightCalculatorDummyCell.contentView.systemLayoutSizeFitting(UILayoutFittingCompressedSize)
        return (size.height + 1)
    }

Handling Table view reloads on notification

deinit {
        NotificationCenter.default.removeObserver(self)
    }

    func didChangePreferredContentSize() {
        maxHeight = getMaxHeight()
        tableView.reloadData()
    }

Final step, returning max heights in tableview delegate

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

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

enter image description here

BangOperator
  • 4,057
  • 2
  • 21
  • 37
0

Make myLabel2 a FixedHeightLabel so that its height is always two lines.

class FixedHeightLabel: TopAlignedLabel {
    override var intrinsicContentSize: CGSize {
        let oldText = text
        text = "\n"
        let height = sizeThatFits(CGSize(width: .max, height: .max)).height
        text = oldText
        return CGSize(width: UIViewNoIntrinsicMetric, height: height)
    }
}

class TopAlignedLabel: UILabel {
    override func drawText(in rect: CGRect) {
        let textRect = super.textRect(forBounds: bounds, limitedToNumberOfLines: numberOfLines)
        super.drawText(in: textRect)
    }
}
ma11hew28
  • 106,283
  • 107
  • 420
  • 616