Search code examples
iosswifttableviewcollectionviewstackview

How to use a variable UIStackView in UITableView or UICollectionView?


I'm currently building a generic form builder using a UICollectionView.

Each form element is a cell, and for checkboxes, I'm using a UIStackView inside the cell to display all the available options.

The problem is that each time I reuse the cell, even if I remove all the arrangedSubviews, they stay in the view with the new one.

The following code is a simplified version of what I'm doing:

class cell: UICollectionViewCell {

    @IBOutlet weak var stackView: UIStackView!

    func setup(options: [String]) {

        for option in options {
            let label = UILabel()
            label.text = option
            stackView.addArrangedSubview(label)
        }

    }

    override func prepareForReuse() {
        super.prepareForReuse()
        optionsStackView.arrangedSubviews.forEach({ optionsStackView.removeArrangedSubview(view: $0) })
    }

}

Instead of that, my current workaround is to hide() each arrangedSubview in prepareForReuse() instead of removing them, but I don't like that.


Solution

  • If you read the Xcode docs on the removeArrangedSubview method, they say:

    Discussion: This method removes the provided view from the stack’s arrangedSubviews array. The view’s position and size will no longer be managed by the stack view. However, this method does not remove the provided view from the stack’s subviews array; therefore, the view is still displayed as part of the view hierarchy.

    To prevent the view from appearing on screen after calling the stack’s removeArrangedSubview: method, explicitly remove the view from the subviews array by calling the view’s removeFromSuperview() method, or set the view’s isHidden property to true.

    So you need to also remove the subviews from the stack view. (I also struggled with this when I first started using stack views.)

    Edit:

    In fact, as @kid_x points out, simply removing the subview with removeFromSuperView() works. I'm not sure what the point of removeArrangedSubview() is, TBH.


    Edit #2:

    I would advise against using removeArrangedSubview() at all. Instead, do one of the following:

    Option 1: If you need to remove a view from a stack view permanently, simply use removeFromSuperView().

    Option 2: If you want to remove views from your stack view and then put them back later, simply toggle the child view's isHidden property, as mentioned in suprandr's answer. The stack view will close up the empty space and reposition the remaining views as if you removed them.