swiftswift4uistackviewswift4.2

Programmatically emptying UIStackView


I have a fairly simple code which, upon clicking a button, adds a randomly colored UIView to a UIStackView, and upon a different button click, removes a random UIView from the UIStackView.

Here's the code:

import UIKit

class ViewController: UIViewController, Storyboarded {
    weak var coordinator: MainCoordinator?
    @IBOutlet weak var stackView: UIStackView!
    
    var tags: [Int] = []
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    
    @IBAction func buttonPressed(_ sender: UIButton) {
        switch sender.tag {
            case 10:
                let view = UIView(frame: CGRect(x: 0, y: 0, width: stackView.frame.width, height: 20))
                var number = Int.random(in: 0...10000)
                
                while tags.contains(number) {
                    number = Int.random(in: 0...10000)
                }
                
                tags.append(number)
                view.tag = number
                view.backgroundColor = .random()
                stackView.addArrangedSubview(view)
            case 20:
                if tags.count == 0 {
                    print("Empty")
                    return
                }
                
                let index = Int.random(in: 0...tags.count - 1)
                let tag = tags[index]
                
                tags.remove(at: index)
                
                if let view = stackView.arrangedSubviews.first(where: { $0.tag == tag }) {
                    stackView.removeArrangedSubview(view)
                }
            default:
                break
        }
    }
}

extension CGFloat {
    static func random() -> CGFloat {
        return CGFloat(arc4random()) / CGFloat(UInt32.max)
    }
}

extension UIColor {
    static func random() -> UIColor {
        return UIColor(
           red:   .random(),
           green: .random(),
           blue:  .random(),
           alpha: 1.0
        )
    }
}

I'm not using removeFromSuperview on purpose - since I would (later) want to reuse those removed UIViews, and that is why I'm using removeArrangedSubview.

The issue I'm facing is:

All UIViews are removed as expected (visually of course, I know they're still in the memory) until I reach the last one - which, even though was removed, still appears and filling the entire UIStackView.

What am I missing here?


Solution

  • You can understand removeArrangedSubview is for removing constraints that were assigned to the subview. Subviews are still in memory and also still inside the parent view.

    To achieve your purpose, you can define an array as your view controller's property, to hold those subviews, then use removeFromSuperview.

    Or use .isHidden property on any subview you need to keep it in memory rather than removing its contraints. You will see the stackview do magical things to all of its subviews.

    let subview = UIView()
    stackView.addArrangedSubview(subview)
    
    func didTapButton(sender: UIButton) {
       subview.isHidden.toggle()
    }
    

    Last, addArrangedSubview will do two things: add the view to superview if it's not in superview's hierachy and add contraints for it.