swiftautolayoutstackview

UILabel disappears in StackView when there is too much content


I have a UIStackView with three subviews in it: a title (UILabel), a body (UILabel), and a post image (UIImageView). This last image only gets added when the post has an imageURL (this is optional.)

Looking below, you can see that when I display the image, the UILabel disappears from out of the stackView somehow. How do I fix this?

P.S. Looking ahead, I am going to want to remove imageViews when the user is scrolling from those posts that lack imageViewURLs. Any tips on how to proceed here? Thank you again in advance.

enter image description here

Below is the relevant code:

class FeedTableViewCell: UITableViewCell {
//MARK: Public properties
var post: Post?{
    didSet{
        guard let post = post else {return}

        // Adding user's name
        let attributedText = NSMutableAttributedString(string: post.author.name + " → " + post.group.name, attributes: [NSAttributedString.Key.font: UIFont.boldSystemFont(ofSize: 14)])

        // Adding date and user's first name
        let dateFormatter = PostDateFormatter()
        dateFormatter.dateStyle = .long
        dateFormatter.timeStyle = .short
        attributedText.append(NSAttributedString(string: "\n" + dateFormatter.string(from: post.createdAt), attributes: [NSAttributedString.Key.font: UIFont.systemFont(ofSize: 12), NSAttributedString.Key.foregroundColor: UIColor(r: 155/255, g: 161/255, b: 171/255)]))

        // Increasing Spacing
        let paragraphStyle = NSMutableParagraphStyle()
        paragraphStyle.lineSpacing = 4
        attributedText.addAttribute(NSAttributedString.Key.paragraphStyle, value: paragraphStyle, range: NSMakeRange(0, attributedText.length))

        titleLabel.attributedText = attributedText

        // Setting profile image
        iconImageView.setImage(for: post.author, setContentMode: .scaleAspectFit)

        messageTextView.text = post.content

        setupImageSubviews()
    }
}

//MARK: Private implementation
private let iconImageView = CircleImageView(size: 44)

private let titleLabel: UILabel = {
    let label = UILabel()
    label.numberOfLines = 0
    label.font = UIFont.systemFont(ofSize: 14)
    label.textColor = .black
    label.translatesAutoresizingMaskIntoConstraints = false
    return label
}()

private let messageTextView: UILabel = {
    let labelView = UILabel()
    labelView.numberOfLines = 0
    labelView.font = UIFont.systemFont(ofSize: 14)
    labelView.translatesAutoresizingMaskIntoConstraints = false
    return labelView
}()

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

}

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

}

var stackView = UIStackView()

func setupDefaultViews(){
    backgroundColor = UIColor.white

    stackView.addArrangedSubview(titleLabel)
    stackView.addArrangedSubview(messageTextView)

    contentView.addSubview(iconImageView)
    contentView.addSubview(stackView)

    iconImageView.anchor(top: contentView.topAnchor, leading: contentView.leadingAnchor, bottom: nil, trailing: nil, padding: .init(top: 0, left: 8, bottom: 0, right: 0), size: CGSize(width: 44, height: 44))

    stackView.anchor(top: contentView.topAnchor, leading: iconImageView.trailingAnchor, bottom: contentView.bottomAnchor, trailing: contentView.trailingAnchor, padding: .init(top: 0, left: 8, bottom: 8, right: 8))
    stackView.axis = .vertical
}

private func setupImageSubviews() {

    guard let imageURL = post?.imageURL else {return} // if no image exists, return, preventing image view from taking extra memory and performance to initialize and calculate constraints

    // initialize here instead of globally, so it doesnt take extra memory holding this when no image exists.
    let messageImageView: UIImageView = {
        let imageView = UIImageView()
        let contentImage = UIImage(systemName: "person.crop.circle.fill")!.withTintColor(.gray).withRenderingMode(.alwaysOriginal)
        imageView.kf.setImage(with: imageURL, placeholder: contentImage)
        imageView.contentMode = .scaleAspectFill
        imageView.layer.masksToBounds = true
        imageView.layer.borderWidth = 1
        imageView.translatesAutoresizingMaskIntoConstraints = false
        return imageView
    }()

    stackView.addArrangedSubview(messageImageView)
}}

Solution

  • Just so people know how I fixed this, I was attempting to use one cell template which would handle two different kinds of cells. That is a mistake, instead use the information in this link Using Auto Layout in UITableView for dynamic cell layouts & variable row heights to properly create two cell templates. I also found this code guaranteed that the cells were properly displaying the titles:

        // Keeps the title text always showing
    override func didMoveToSuperview() {
        super.didMoveToSuperview()
        layoutIfNeeded()
    }