Zack Shapiro
Zack Shapiro

Reputation: 6998

How do I get UIView width and height before adding to subview?

I'm building a UIPageViewController that has a variable number of pages based on the height of views in an array of views.

I have a class called a BlockView that looks like this:

final class BlockView: UIView {

    init(viewModel: BlockViewModel) {
        super.init(frame: .zero)

        let primaryLabel = UILabel()
        primaryLabel.text = viewModel.labelText
        addSubview(primaryLabel)

        constrain(primaryLabel) {
            $0.top == $0.superview!.top + 8
            $0.bottom == $0.superview!.bottom - 8
            $0.left == $0.superview!.left + 8
            $0.right == $0.superview!.right - 8
        }  
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

What I'd like to be able to do is loop through my array of BlockViews and run print(blockView.frame) and see frames that aren't zero.

Now I know I'm setting my frame to .zero inside of the BlockView.init. That's because I'd like the view to size itself based on its labels.

Is there a function I need to run to achieve this?

Thanks

Upvotes: 1

Views: 2635

Answers (1)

Milan Nosáľ
Milan Nosáľ

Reputation: 19767

Try sizeThatFits(_:) to calculate it without putting it to the superview. The only parameter to the method is the CGSize that represents boundaries in which it should be displayed. E.g., if you know the width of the superview (e.g., 340 points) and you want to know how much it will take in height:

let expectedSize = view.sizeThatFits(CGSize(width: 340, height: .greatestFiniteMagnitude))

However, your BlockView does not seem to have a proper constraints set yet. You initialize it with super.init(frame: .zero) - thus it has size 0,0.

And your constraints does not change that, e.g.:

constrain(primaryLabel) {
   $0.centerY == $0.superview!.centerY
   $0.left == $0.superview!.left + 8
}

This looks like you set the center in Y axis of the label to the center of the block view, and the left anchor of the label to the left anchor of the view. If the blockView would have the size already, that would position the label properly. But right now the size of the block view is not affected by the size of labels at all. I guess you would want to constrain the labels to the left, right, top and bottom anchors of the blockView, so that when you try to calculate the size of the blockView, the autolayout will have to first calculate the size of the labels and based on this the size of the blockView itself.

A possible solution (I am using anchor based autolayout syntax) that you can try to put to initializer of the BlockView:

primaryLabel.leftAnchor.constraint(equalTo: self.leftAnchor, constant: 8).isActive = true
primaryLabel.topAchor.constraint(equalTo: self.topAnchor, constant: 8).isActive = true
primaryLabel.rightAnchor.constraint(equalTo: self.rightAnchor, constant: -8).isActive = true
primaryLabel.bottomAnchor.constraint(equalTo: secondaryLabel.topAnchor, constant: -8).isActive = true
secondaryLabel.leftAnchor.constraint(equalTo: self.leftAnchor, constant: 8).isActive = true
secondaryLabel.rightAnchor.constraint(equalTo: self.rightAnchor, constant: -8).isActive = true
secondaryLabel.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -8).isActive = true

Upvotes: 1

Related Questions