0
votes

I need to render an image within a UITableViewCell.

The API returns the original height and width of the image, so I believe I should be able to calculate the ratio. However I am unsure how to lay this out using autolayout.

final class ContentArticleImage: UITableViewCell {

  var ratio: CGFloat? { // 1000 / 600 (width / height)
    didSet {
      guard let ratio = ratio else { return }
      contentImageView.heightAnchor.constraint(equalTo: widthAnchor, multiplier: 1 / ratio).isActive = true
    }
  }

  private lazy var contentImageView = configure(UIImageView(frame: .zero), using: {
    $0.translatesAutoresizingMaskIntoConstraints = false
    $0.backgroundColor = .darkGray
    $0.contentMode = .scaleAspectFit
  })

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

  required init?(coder: NSCoder) {
    return nil
  }
}
private extension ContentArticleImage {
  func configureUI() {

    addSubview(contentImageView)
    NSLayoutConstraint.activate([
      contentImageView.topAnchor.constraint(equalTo: topAnchor, constant: 12),
      contentImageView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 12),
      contentImageView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -12),
      contentImageView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -12)
    ])

  }
}

I tried something like the above, but I can an autolayout error

(
    "<NSLayoutConstraint:0x600002885b30 V:|-(12)-[UIImageView:0x7ffe3551e170]   (active, names: '|':OneHubApp.ContentArticleImage:0x7ffe3551ddc0'ContentArticleImage' )>",
    "<NSLayoutConstraint:0x600002885c70 UIImageView:0x7ffe3551e170.bottom == OneHubApp.ContentArticleImage:0x7ffe3551ddc0'ContentArticleImage'.bottom - 12   (active)>",
    "<NSLayoutConstraint:0x600002885ea0 UIImageView:0x7ffe3551e170.height == 0.548298*OneHubApp.ContentArticleImage:0x7ffe3551ddc0'ContentArticleImage'.width   (active)>",
    "<NSLayoutConstraint:0x6000028860d0 'UIView-Encapsulated-Layout-Height' OneHubApp.ContentArticleImage:0x7ffe3551ddc0'ContentArticleImage'.height == 251   (active)>",
    "<NSLayoutConstraint:0x600002886080 'UIView-Encapsulated-Layout-Width' OneHubApp.ContentArticleImage:0x7ffe3551ddc0'ContentArticleImage'.width == 414   (active)>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x600002885c70 UIImageView:0x7ffe3551e170.bottom == OneHubApp.ContentArticleImage:0x7ffe3551ddc0'ContentArticleImage'.bottom - 12   (active)>

Using the original height / width how should I calculate the height of an image in my cell?

EDIT My cell is rendered using the following method:

  override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "CONTENT_CELL", for: indexPath) as! ContentArticleImage
    cell.ratio = asset.width / asset.height
    cell.selectionStyle = .none
    return cell
  }
1
You need to show more code. How and where are you setting the ratio?Rob
Sorry, the height / width comes from the api response and and when the cell is dequeued in cellForRowAt I set the value on var ratio: CGFloat? { // 1000 / 600 (width / height) }Harry Blue
I'm 99% sure I know what your problem is, but without seeing your cellForRowAt method I can't post a solution. Perhaps you can update your post with the code. Also, it is unclear what you mean by "original height / width"Rob
I shall add it now :)Harry Blue
I've added it to the bottom of the postHarry Blue

1 Answers

0
votes

What you need to do is deactivate your height constraint each time the cell is reused. Also, you need to set the height constraint equal to a multiple of the contentImageView width constraint, not the cell's width constraint. So your code should look something like this:

final class ContentArticleImage: UITableViewCell {

    var heightConstraint: NSLayoutConstraint?

    var ratio: CGFloat? { // 1000 / 600 (width / height)
        didSet {
            guard let ratio = ratio else { return }
            heightConstraint?.isActive = false
            heightConstraint = contentImageView.heightAnchor.constraint(equalTo: contentImageView.widthAnchor, multiplier: 1 / ratio)
            heightConstraint?.isActive = true
            heightConstraint?.priority = .defaultHigh
        }
    }

    lazy var contentImageView: UIImageView = {
        let imageView = UIImageView()
        imageView.translatesAutoresizingMaskIntoConstraints = false
        imageView.backgroundColor = .darkGray
        imageView.contentMode = .scaleAspectFit
        return imageView
    }()

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

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

Note that I'm setting the priority to .defaultHight just to silence a layout warning in the console.