0

So I'm trying to see how I can do following ui in code by using UIStackView:

enter image description here

Notice that the image can be hidden or can even have a different size. The only thing that is constant about the image is the width.

Following is the class that I came up with:

public class MenuItemTableViewCell: UITableViewCell {

    //MARK: UI elements
    var titleLabel = UILabel()
    var detailsLabel = UILabel()
    var itemImageView = UIImageView()
    var mainStackView = UIStackView()

    //MARK: Variables
    var menuItem: MenuItem? {
        didSet {
            if let item = menuItem {
                titleLabel.text = item.name
                detailsLabel.text = item.itemDescription
                if let imageURL = item.imageURL {
                    itemImageView.af_setImage(withURL: imageURL) { [weak self] response in

                        guard let imageView = self?.itemImageView else {
                            return
                        }

                        switch response.result {
                        case .success(let image):

                            // Instance variable:
                            var imageAspectRatioConstraint: NSLayoutConstraint?

                            // When you set the image:
                            imageAspectRatioConstraint?.isActive = false
                            imageAspectRatioConstraint = imageView.heightAnchor.constraint(
                                equalTo: imageView.widthAnchor,
                                multiplier: 100 / image.size.height)
                            imageAspectRatioConstraint!.isActive = true
                        default:
                            return
                        }
                    }
                } else {
                    itemImageView.isHidden = true
                }
            } else {
                titleLabel.text = ""
                detailsLabel.text = ""
                itemImageView.image = nil
            }
        }
    }

    override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)

        initViews()
    }

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


    private func initViews() {
        self.accessoryType = .disclosureIndicator
        self.selectionStyle = .none

        setupMainStackView()
        setupTitleLabel()
        setupDetailLabel()
        setupItemImageViewLabel()
    }

    //MARK: Components setup
    private func setupMainStackView() {
        mainStackView.alignment = .fill
        mainStackView.distribution = .fillProportionally
        mainStackView.spacing = 5
        mainStackView.axis = .vertical
        mainStackView.translatesAutoresizingMaskIntoConstraints = false
        mainStackView.layoutMargins = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)
        mainStackView.isLayoutMarginsRelativeArrangement = true
        mainStackView.setContentCompressionResistancePriority(UILayoutPriorityRequired, for: .vertical)
        mainStackView.distribution = .fillProportionally
        self.contentView.addSubview(mainStackView)

        addMainStackViewContraints()
    }

    //
    // Title Label
    //
    private func setupTitleLabel() {

        titleLabel.textColor = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 1)
        titleLabel.numberOfLines = 1
        mainStackView.addArrangedSubview(titleLabel)

        addTitleLabelConstraints()
    }

    //
    // Detail Label
    //
    private func setupDetailLabel() {
        detailsLabel.textColor = #colorLiteral(red: 0.501960814, green: 0.501960814, blue: 0.501960814, alpha: 1)

        detailsLabel.setContentHuggingPriority(UILayoutPriorityRequired, for: .vertical)
        detailsLabel.numberOfLines = 2
        mainStackView.addArrangedSubview(detailsLabel)

        addDetailsLabelContraints()
    }

    //
    // Item Image
    //
    private func setupItemImageViewLabel() {
        itemImageView.contentMode = .scaleAspectFit
        itemImageView.clipsToBounds = true
        mainStackView.addArrangedSubview(itemImageView)

        addItemImageViewConstraitns()
    }

    //MARK: Constraints setup
    private func addTitleLabelConstraints() {
        titleLabel.translatesAutoresizingMaskIntoConstraints = false

        titleLabel.setContentCompressionResistancePriority(UILayoutPriorityRequired, for: .vertical)
        titleLabel.setContentHuggingPriority(UILayoutPriorityRequired, for: .vertical)
    }

    func addDetailsLabelContraints() {
        detailsLabel.translatesAutoresizingMaskIntoConstraints = false

        detailsLabel.setContentCompressionResistancePriority(UILayoutPriorityRequired, for: .vertical)
        detailsLabel.setContentHuggingPriority(UILayoutPriorityRequired, for: .vertical)
    }

    func addItemImageViewConstraitns() {
        itemImageView.translatesAutoresizingMaskIntoConstraints = false
        itemImageView.widthAnchor.constraint(equalTo: mainStackView.widthAnchor).isActive = true
        itemImageView.leftAnchor.constraint(equalTo: mainStackView.leftAnchor).isActive = true
    }

    private func addMainStackViewContraints() {
        mainStackView.topAnchor.constraint(equalTo: self.contentView.topAnchor).isActive = true
        mainStackView.bottomAnchor.constraint(equalTo: self.contentView.bottomAnchor).isActive = true
        mainStackView.leftAnchor.constraint(equalTo: self.contentView.leftAnchor).isActive = true
        mainStackView.rightAnchor.constraint(equalTo: self.contentView.rightAnchor).isActive = true
    }
}

Currently it looks like this:

enter image description here

and if I remove the bottom constraint from my UIStackView, it'd be like this:

enter image description here

Any help on getting a better understanding on how UIStackView works and having a auto-resize one in UITableViewCell is highly appreciated

manman
  • 4,743
  • 3
  • 30
  • 42
  • As far as I know you only want to resize and constrain the stack view itself. For modifying views inside you need to use the stack view methods. Otherwise just make custom view to contain your stacked views and constrain normally. – Aquila Sagitta Aug 15 '17 at 20:09
  • multiplier: 100 / image.size.height) Is it possible the image height will ever be 0? Not related to the original question. – Kibitz503 Aug 16 '17 at 00:07

2 Answers2

0

What does your logic look like in:

- (CGSize) collectionView:(UICollectionView *)collectionView
                   layout:(UICollectionViewLayout*)collectionViewLayout
   sizeForItemAtIndexPath:(NSIndexPath *)indexPath

Should be something like...

Kibitz503
  • 867
  • 6
  • 10
-1

In general, using UIStackView in UITableViewCell is not a good idea. It may cause performance issues. But if you like you can try calling layoutIfNeeded() method of your cell when you are configuring it (especially after setting image view of the cell). Maybe it’ll help.

Artem Kirillov
  • 1,132
  • 10
  • 25
  • Yeah tried that one and it didn't work. It's been much easier to use simple Layout especially for auto-resizing cells. Was just curious if such a behavior can be achieved and how – manman Aug 15 '17 at 20:34