Search code examples
swiftuitableviewautolayout

Auto layout with custom UITableViewCell without storyboard programmatically (Swift 5)


I am having some difficulty with Auto Layout within the confines of a UITableViewCell. I am fairly new to Swift so any help would be appreciated. I am trying to create a custom cell where the cell contains three UIImageView objects arranged as shown.

=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  +----------------------------------+   +-----------------------------------+
  |                                  |   |                                   |
  |                                  |   |                                   |
  |                                  |   |                                   |
  |                                  |   |                                   |
  |                                  |   |                                   |
  |                                  |   |               view2               |
  |                                  |   |                                   |
  |                                  |   |                                   |
  |                                  |   |                                   |
  |                                  |   |                                   |
  |                                  |   +-----------------------------------+
  |               view1              |
  |                                  |   +-----------------------------------+
  |                                  |   |                                   |
  |                                  |   |                                   |
  |                                  |   |                                   |
  |                                  |   |                                   |
  |                                  |   |               view3               |
  |                                  |   |                                   |
  |                                  |   |                                   |
  |                                  |   |                                   |
  |                                  |   |                                   |
  |                                  |   |                                   |
  +----------------------------------+   +-----------------------------------+
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

But what I'm actually getting is the following where view1, view2, and view3 are properly aligned but they are not anchored to the contentView of the cell on the bottom and since it is a resizable cell and nothing is pinned to the bottom it ignores the content and fixes itself to the estimated 44 high estimated cell height.

=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  +----------------------------------+   +-----------------------------------+
  |                                  |   |                                   |
  |                                  |   |                                   |
  |                                  |   |                                   |
  |                                  |   |                                   |
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  |                                  |   |               view2               |
  |                                  |   |                                   |
  |                                  |   |                                   |
  |                                  |   |                                   |
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  |                                  |   +-----------------------------------+
  |               view1              |
  |                                  |   +-----------------------------------+
  |                                  |   |                                   |
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  |                                  |   |                                   |
  |                                  |   |                                   |
  |                                  |   |               view3               |
  |                                  |   |                                   |
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  |                                  |   |                                   |
  |                                  |   |                                   |
  |                                  |   |                                   |
  +----------------------------------+   +-----------------------------------+
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

The code below includes a .anchor method as a UIView extension for handling anchors more efficiently but otherwise you get the point.

import UIKit

class GalleryTableCell: UITableViewCell, SelfConfiguringCell {

    lazy var view1: UIView = {
        let view = UIView()
        view.backgroundColor = .red
        view.translatesAutoresizingMaskIntoConstraints = false
        return view
    }()

    lazy var view2: UIView = {
        let view = UIView()
        view.backgroundColor = .blue
        view.translatesAutoresizingMaskIntoConstraints = false
        return view
    }()

    lazy var view3: UIView = {
        let view = UIView()
        view.backgroundColor = .green
        view.translatesAutoresizingMaskIntoConstraints = false
        return view
    }()

    static var identifier: String {
        return String(describing: self)
    }

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

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    private func setupCellView() {
        backgroundColor = .systemBackground
        [view1, view2, view3].forEach { ( addSubview($0) ) }

        view1.anchor(top: topAnchor, bottom: nil, leading: nil, trailing: trailingAnchor, with: UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 6), size: CGSize(width: 175, height: 0))

        view1.heightAnchor.constraint(equalTo: view1.widthAnchor).isActive = true

        view2.anchor(top: view1.bottomAnchor, bottom: nil, leading: nil, trailing: view1.trailingAnchor, with: UIEdgeInsets(top: 6, left: 0, bottom: 0, right: 0))
        view2.resize(to: view1)

        view3.anchor(top: topAnchor, bottom: view2.bottomAnchor, leading: leadingAnchor, trailing: view1.leadingAnchor, with: UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 6))

    }

    func configure(with app: Any) {}

}

Because I'm using auto dimension to auto-size the cell the above line is required to constrain the view into the cell, otherwise the cell will size to the estimated height (44) and the three views will extend covering over a number of cells below it.

Help understanding what I'm doing wrong would be much appreciated.


Solution

  • To get automatic sizing, you need to do 3 things:

    1. Set estimatedRowHeight to whatever you think the size will be. It doesn't have to be exact, but it helps with performance.
    2. Set rowHeight to UITableView.automaticDimension.
    3. Set top & bottom constraints of the view as well as a height constraint.