Home > OS >  Self sizing cell with multiple Stack Views
Self sizing cell with multiple Stack Views

Time:09-17

I've looked all over the forum and attempted all the solutions and thus far nothing has worked. I noticed my UIImageView was overlaying multiple cells, meaning the celll did not automatically adjust its height. Here is the constraint i found in the console it complained about.

"<NSLayoutConstraint:0x600001970f50 'UIView-Encapsulated-Layout-Height' UITableViewCellContentView:0x7f86a4813dd0.height == 44 (active)>"

In my tableViewController I have the follow

  tableView.rowHeight = UITableView.automaticDimension
  tableView.estimatedRowHeight = 300

Here is my entire cell that should self size.

import UIKit

class UserConnectionCell: UITableViewCell {

fileprivate let leftImageView: UIImageView = {
    let uiImageView = UIImageView()
    uiImageView.translatesAutoresizingMaskIntoConstraints = false

   return uiImageView
}()

fileprivate let leftLabel: UILabel = {
    let label = UILabel()
    label.translatesAutoresizingMaskIntoConstraints = false
    return label
}()

fileprivate let middleLabel: UILabel = {
    let label = UILabel()
    label.numberOfLines = 0
    label.font = UIFont(name: "Ariel", size: 10)
    label.textAlignment = .center
    label.translatesAutoresizingMaskIntoConstraints = false
    return label
}()

fileprivate let rightImageView: UIImageView = {
    let uiImageView = UIImageView()
    uiImageView.translatesAutoresizingMaskIntoConstraints = false
   return uiImageView
}()

fileprivate let rightLabel: UILabel = {
    let label = UILabel()
    label.translatesAutoresizingMaskIntoConstraints = false
    return label
}()

fileprivate let stackViewLeft: UIStackView = {
    let stackView = UIStackView()
    stackView.axis = .vertical
    stackView.translatesAutoresizingMaskIntoConstraints = false
    return stackView
}()
fileprivate let stackViewRight: UIStackView = {
    let stackView = UIStackView()
    stackView.axis = .vertical
    stackView.translatesAutoresizingMaskIntoConstraints = false
    return stackView
}()
fileprivate let stackViewMain: UIStackView = {
    let stackView = UIStackView()
    stackView.axis = .horizontal
    stackView.alignment = .fill
    stackView.spacing = 0
    stackView.translatesAutoresizingMaskIntoConstraints = false
    return stackView
}()

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

    stackViewLeft.addArrangedSubview(leftImageView)
    stackViewLeft.addArrangedSubview(leftLabel)

    stackViewRight.addArrangedSubview(rightImageView)
    stackViewRight.addArrangedSubview(rightLabel)

    stackViewMain.addArrangedSubview(stackViewLeft)
    stackViewMain.addArrangedSubview(middleLabel)
    stackViewMain.addArrangedSubview(stackViewRight)

    contentView.addSubview(stackViewMain)
}
// called when trying to layout subviews.
override func layoutSubviews() {
    super.layoutSubviews()

    stackViewLeft.addConstraint(NSLayoutConstraint(item: leftImageView, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .width, multiplier: 1, constant: 100))
    stackViewLeft.addConstraint(NSLayoutConstraint(item: leftImageView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .height, multiplier: 1, constant: 100))

    stackViewRight.addConstraint(NSLayoutConstraint(item: rightImageView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .height, multiplier: 1, constant: 100))
    stackViewRight.addConstraint(NSLayoutConstraint(item: rightImageView, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .width, multiplier: 1, constant: 100))

    NSLayoutConstraint.activate(
        [stackViewMain.topAnchor.constraint(equalTo: contentView.topAnchor,constant: 0),
         stackViewMain.leadingAnchor.constraint(equalTo: contentView.leadingAnchor,constant: 0),
         stackViewMain.trailingAnchor.constraint(equalTo: contentView.trailingAnchor,constant: 0),
         stackViewMain.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: 0)
        ])

}

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

var viewModel : UserConnectionViewModel? {
    didSet {
        // move this to the view model
        if let profileUrl = viewModel?.leftImageUrl {
            leftImageView.loadImageFromURL(url: profileUrl)
        } else {
            leftImageView.image = UIImage(named: "defaultprofile")
        }

        if let profileUrl = viewModel?.rightImageUrl {
            rightImageView.loadImageFromURL(url: profileUrl)
        } else {
            rightImageView.image = UIImage(named: "defaultprofile")
        }
        leftLabel.text = viewModel?.leftLabel
        middleLabel.text = viewModel?.middleLabel
        rightLabel.text = viewModel?.rightlabel
    }
}

override func awakeFromNib() {
    super.awakeFromNib()
    self.contentView.autoresizingMask = .flexibleHeight
    // Initialization code
}

override func setSelected(_ selected: Bool, animated: Bool) {
    super.setSelected(selected, animated: animated)

    // Configure the view for the selected state
}
}

Any ideas for why the cell is not self sizing?

CodePudding user response:

First, a cell's contentView is a "special" view with properties integral to the table view's operation.

So, do not do this:

self.contentView.autoresizingMask = .flexibleHeight

Second, layoutSubviews() can be (and usually is) called multiple times during the lifecycle of a cell / view. Your constraint setup should be done in init:

override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
    super.init(style: style, reuseIdentifier:reuseIdentifier)
    
    stackViewLeft.addArrangedSubview(leftImageView)
    stackViewLeft.addArrangedSubview(leftLabel)
    
    stackViewRight.addArrangedSubview(rightImageView)
    stackViewRight.addArrangedSubview(rightLabel)
    
    stackViewMain.addArrangedSubview(stackViewLeft)
    stackViewMain.addArrangedSubview(middleLabel)
    stackViewMain.addArrangedSubview(stackViewRight)
    
    contentView.addSubview(stackViewMain)
    
    NSLayoutConstraint.activate([
        
        // constrain main stack view to all 4 sides of contentView
        stackViewMain.topAnchor.constraint(equalTo: contentView.topAnchor,constant: 0),
        stackViewMain.leadingAnchor.constraint(equalTo: contentView.leadingAnchor,constant: 0),
        stackViewMain.trailingAnchor.constraint(equalTo: contentView.trailingAnchor,constant: 0),
        stackViewMain.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: 0),

        // constrain left image view Width: 100-pts,
        //  Height equal to Width (1:1 ratio)
        leftImageView.widthAnchor.constraint(equalToConstant: 100.0),
        leftImageView.heightAnchor.constraint(equalTo: leftImageView.widthAnchor),
        
        // constrain right image view Width: 100-pts,
        //  Height equal to Width (1:1 ratio)
        rightImageView.widthAnchor.constraint(equalToConstant: 100.0),
        rightImageView.heightAnchor.constraint(equalTo: rightImageView.widthAnchor),
        
    ])
    
}

So... replace your init with the above code and completely remove both your awakeFromNib() and layoutSubviews() funcs.

You should get this:

enter image description here

  • Related