Get the frame of a UIStackView subViews

Wozzer picture Wozzer · Mar 19, 2017 · Viewed 8.6k times · Source

I have created a UIStackView in IB which has the distribution set to Fill Equally. I am looking to get the frame for each subView but the following code always returns (0, 0, 0, 0).

class ViewController: UIViewController {
    @IBOutlet weak var stackView: UIStackView!

    override func viewDidLoad() {
        super.viewDidLoad()

        let pView = UIView()
        let sView = UIView()

        pView.backgroundColor = UIColor.red
        sView.backgroundColor = UIColor.orange

        stackView.addArrangedSubview(pView)
        stackView.addArrangedSubview(sView)
    }

    override func viewDidLayoutSubviews() {
        print(stackView.arrangedSubviews[0].frame)
        print(stackView.arrangedSubviews[1].frame)
    }
}

I would think that a stack view set to fill equally would automatically set the calculate it.

Any help would be appreciated.

Answer

agibson007 picture agibson007 · Mar 20, 2017

After reading over your code I think this is just a misunderstanding of viewDidLayoutSubviews(). Basically it is called when all the views that are descendants of the main view have been laid out but this does not include the subviews(descendants) of these views. See discussion notes from Apple.

"When the bounds change for a view controller's view, the view adjusts the positions of its subviews and then the system calls this method. However, this method being called does not indicate that the individual layouts of the view's subviews have been adjusted. Each subview is responsible for adjusting its own layout."

Now there are many ways to get the frame of the subviews with this being said.

First you could add one line of code in viewdidload and get it there.

    override func viewDidLoad() {
    super.viewDidLoad()

    let pView = UIView()
    let sView = UIView()

    pView.backgroundColor = UIColor.red
    sView.backgroundColor = UIColor.orange

    stackView.addArrangedSubview(pView)
    stackView.addArrangedSubview(sView)
    stackView.layoutIfNeeded()
    print(stackView.arrangedSubviews[0].frame)
    print(stackView.arrangedSubviews[1].frame)

}

OR you can wait until viewDidAppear and check there.

 override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    print(stackView.arrangedSubviews[0].frame)
    print(stackView.arrangedSubviews[1].frame)
}