2

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.

jdanthinne
  • 337
  • 1
  • 2
  • 9

2 Answers2

7

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.

Duncan C
  • 128,072
  • 22
  • 173
  • 272
  • Note that calling removeFromSuperview() on the subview in question is sufficient: https://developer.apple.com/documentation/uikit/uistackview/1616232-arrangedsubviews – kid_x Feb 23 '18 at 18:59
2

In many cases, since removing from a superview in a reusable view could (and mostly, will) cause the view to be released, in a UIStackView simply putting

dynamicView.isHidden = false

to remove and

dynamicView.isHidden = true

to add again, will cause the stack view to rearrange the other arranged views.

superandrew
  • 1,741
  • 19
  • 35