Get the frame of a UIStackView subViews
Asked Answered
A

1

7

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.

Ashcraft answered 19/3, 2017 at 18:4 Comment(2)
Yes. The layout is fine. I want the option to hide and display each subview with 3 possible options. Only pView is shown, only sView is shown and then both views are shown. The reason I need to get the subviews frame/bounds is that I want to overlay a draw view to draw on top of the image in each view. It will work fine if switching between each view with only one view showing but if both subviews are displayed, their frame size changes and I will need to adjust the draw view frame. I was hoping the above code would give me the frame but it doesn't.Ashcraft
Did you check my answer. Also you can animate the hidden property of a stackview subview.Addlepated
A
24

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)
}
Addlepated answered 19/3, 2017 at 23:32 Comment(3)
Thank you. This helped a lot.Ashcraft
Note that the arranged subviews (at least as of iOS 13) will not have their frames figured out at the time viewDidAppear gets called if you have setup the UIStackView in code, rather than in IB. I have found that checking in a dispatch queue after 0.05 seconds seems to guarantee the frames are not (0, 0, 0, 0)... as long as they are supposed to not be. :)Brenda
@D.Pratt is right about frame not being ready at viewDidAppear. What worked for me is calling layoutIfNeeded on the stackview in viewDidLayoutSubviews.Lelahleland

© 2022 - 2024 — McMap. All rights reserved.