Troy
Troy

Reputation: 51

Why does parent UIStackView not recognize child StackView's height?

I am trying to load a UIStackView from a xib that contains multiple labels into a parent UIStackView. The labels in the child stack get populated with values (or hidden) after the ViewController in the viewDidLoad method via a model object.

I expect that the parent stackview would recognize the change in intrinsic height of the child stackview, and thus move sibling views down. However, the next view (button) covers the content of the child stack. Why does the parent not recognize this change to a subview's height? I am not seeing any error messages, ambiguous constraints, or conflicts.

Here is how the error renders and how the views appear in the View Hierarchy. enter image description here

I have tried:

Here is the storyboard layout Storyboard showing parent stack (master stack) and child (Info View)

Parent View Controller

class AcceptTermsViewController: RegistrationViewController {
    
    @IBOutlet weak var infoView: StackViewInBorderedView!
    @IBOutlet weak var termsView: TermsTextView!
    @IBOutlet weak var masterStack: UIStackView!
    
    var registration: Registration?

    override func viewDidLoad() {
        super.viewDidLoad()
        self.configView()
        self.setupNavBar()
    }

    func configView() {
        if let reg = registration {
            infoView.configView(reg)
        }
    }
}

Child StackView Code

class StackViewInBorderedView: UIStackView {
    // loaded from NIB
    private weak var view: UIView!
    @IBOutlet weak var nameLabel: UILabel!
    @IBOutlet weak var titleLabel: UILabel!
    @IBOutlet weak var emailLabel: UILabel!
    @IBOutlet weak var locationLabel: UILabel!
    @IBOutlet weak var postalLabel: UILabel!
    @IBOutlet weak var idLabel: UILabel!
    @IBOutlet weak var npiLabel: UILabel!
    
    
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        loadViewFromNib()
    }
    
    required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        loadViewFromNib()
    }
    
    fileprivate func loadViewFromNib() {
        self.view = Bundle (for: type(of: self)).loadNibNamed(
            "StackViewInBorderedView", owner: self, options: nil)! [0] as? UIView
        view.frame = bounds
        view.autoresizingMask = [.flexibleWidth]
        self.addSubview(view)
    }
    
    override func layoutSubviews() {
        super.layoutSubviews()
        self.layer.cornerRadius = 10
        let shadowLayer = CAShapeLayer()
        shadowLayer.path = UIBezierPath(roundedRect: self.bounds, cornerRadius: self.frame.height / 12).cgPath
        shadowLayer.fillColor = UIColor.white.cgColor
        shadowLayer.shadowColor = UIColor.gray.cgColor
        shadowLayer.shadowOffset = CGSize.zero
        shadowLayer.shadowOpacity = 1
        shadowLayer.shadowRadius = 3
        shadowLayer.masksToBounds = false
        self.layer.insertSublayer(shadowLayer, at: 0)
    }
    
    func configView(_ reg: Registration) {
        configLabels(reg)
        
        isLayoutMarginsRelativeArrangement = true
        directionalLayoutMargins = NSDirectionalEdgeInsets(top: 20, leading: 20, bottom: 20, trailing: 0)
        
    }
    
    func configLabels(_ reg: Registration) {
        
        // Name line
        if let degree = reg.degree {
            nameLabel.text = "\(reg.firstName!) \(reg.lastName!), \(degree)"
        } else {
            nameLabel.text = "\(reg.firstName!) \(reg.lastName!)"
        }
        
        // Title line
        if let title = reg.title {
            titleLabel.text = title
        } else {
            titleLabel.isHidden = true
        }
        
        // Email line
        if let email = reg.email {
            emailLabel.text = email
        } else {
            emailLabel.isHidden = true
        }
        
        // Location line
        if let city = reg.city, let state = reg.state, let postal = reg.postalCode,
            !city.isEmpty, !state.isEmpty, !postal.isEmpty {
            postalLabel.text = "\(city), \(state) \(postal)"
        } else if let postal = reg.postalCode {
            postalLabel.text = postal
        }
        
        // NPI line
        if let licenseId = reg.licenseId {
            switch reg.countryCode {
            case "BR":
                idLabel.text = "ID"
            default:
                idLabel.text = "NPI"
            }
            npiLabel.text = licenseId
        } else {
            self.idLabel.isHidden = true
            self.npiLabel.isHidden = true
        }
    }
}

Upvotes: 1

Views: 659

Answers (1)

Troy
Troy

Reputation: 51

A colleague of mine found the answer.

When loading the view from the nib, I needed to call self.addArrangedSubview instead of self.addSubview which provides the intended behavior.

Upvotes: 1

Related Questions