iosswiftuikitconstraints

How to make UIView fit the size of its contents?


I have the following code

 func setupUI() {
        self.view.backgroundColor = .white
        
        let titleLbl = UILabel()
        titleLbl.text = book.title
        titleLbl.textColor = .black
        titleLbl.textAlignment = .center
        titleLbl.numberOfLines = 0
        titleLbl.lineBreakMode = .byWordWrapping
        titleLbl.font = UIFont(name: "Inter-Regular", size: 16);
        
        view.addSubview(titleLbl)
        
        // Using Auto Layout
        titleLbl.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            titleLbl.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            titleLbl.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20),
            titleLbl.heightAnchor.constraint(equalToConstant: 20)
        ])
        
        innerView = UIView()
        innerView.backgroundColor = UIColor(red: 0.92, green: 0.96, blue: 0.99, alpha: 1.00)
        innerView.layer.cornerRadius = 12
        innerView.translatesAutoresizingMaskIntoConstraints = false
        self.view.addSubview(innerView)
        
        NSLayoutConstraint.activate([
            innerView.topAnchor.constraint(equalTo: titleLbl.bottomAnchor, constant: 40),
            innerView.heightAnchor.constraint(equalToConstant: 300),
            innerView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 20),
            innerView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: -20)
        ])
        
        let backBtn = UIButton(frame: CGRect(x: self.view.frame.width - 60, y: 55, width: 40, height: 40))
        backBtn.setImage(UIImage.init(named: "close-circle"), for: .normal)
        backBtn.backgroundColor = UIColor.clear
        backBtn.addTarget(self, action: #selector(goback), for: .touchUpInside)
        self.view.addSubview(backBtn)

    
        let quote = self.book.quotes![self.currentIndex]
        
        let bodyLbl = UILabel()
        bodyLbl.text = quote
        bodyLbl.textColor = .black
        bodyLbl.textAlignment = .center
        bodyLbl.numberOfLines = 0
        bodyLbl.lineBreakMode = .byWordWrapping
        bodyLbl.font = UIFont(name: "Inter-Regular", size: 16);
        
        innerView.addSubview(bodyLbl)
    
        
        // Using Auto Layout
        bodyLbl.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            bodyLbl.leadingAnchor.constraint(equalTo: innerView.safeAreaLayoutGuide.leadingAnchor, constant: 10),
            bodyLbl.topAnchor.constraint(equalTo: innerView.safeAreaLayoutGuide.topAnchor, constant: 20),
            bodyLbl.trailingAnchor.constraint(equalTo: innerView.safeAreaLayoutGuide.trailingAnchor, constant: -10)
        ])
        bodyLbl.sizeToFit()
        
        
        let shareButton = UIButton()
        shareButton.setTitle("Share", for: .normal)
        shareButton.setTitleColor(.white, for: .normal)
        shareButton.backgroundColor = .black
        shareButton.addTarget(self, action: #selector(self.share), for: .touchUpInside)
        shareButton.layer.cornerRadius = 12
        shareButton.clipsToBounds = true
        shareButton.isUserInteractionEnabled = true
        innerView.addSubview(shareButton)
        
        shareButton.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            shareButton.centerXAnchor.constraint(equalTo: innerView.centerXAnchor),
            shareButton.topAnchor.constraint(equalTo: bodyLbl.bottomAnchor, constant: 40),
            shareButton.heightAnchor.constraint(equalToConstant: 30),
            shareButton.widthAnchor.constraint(equalToConstant: 150)
        ])
        
        let count = self.book.quotes!.count
        if ((self.currentIndex + 1) > count) {
            self.nextBtn.isHidden = true
        } else {
            self.nextBtn.isHidden = false
        }
        
        innerView.setNeedsLayout()
        innerView.layoutIfNeeded()

        let newSize = innerView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize)
        innerView.frame.size = newSize
        innerView.sizeToFit()
    }

How can I make the innerView fit the size of it's contents? I set the height to 300 but I need it to fit the size of label and share button afterwards. Can someone help? What am I doing wrong? I tried sizeToFit but that doesn't work.


Solution

  • You can remove the constant height constraint, and add

    shareButton.bottomAnchor.constraint(equalTo: innerView.safeAreaLayoutGuide.bottomAnchor, constant: -20)
    

    With this constraint, there is now enough information for AutoLayout to determine the height of innerView, because it knows


    I'd also recommend using UIStackView for this kind of layout. You don't need to add any constraints to bodyLbl or shareButton - just do:

    let stackView = UIStackView(arrangedSubviews: [bodyLbl, shareButton])
    stackView.axis = .vertical
    stackView.alignment = .center
    stackView.setCustomSpacing(40, after: bodyLbl)
    innerView.addSubview(stackView)
    
    stackView.translatesAutoresizingMaskIntoConstraints = false
    NSLayoutConstraint.activate([
        stackView.leadingAnchor.constraint(equalTo: innerView.safeAreaLayoutGuide.leadingAnchor, constant: 10),
        stackView.trailingAnchor.constraint(equalTo: innerView.safeAreaLayoutGuide.trailingAnchor, constant: -10),
        stackView.topAnchor.constraint(equalTo: innerView.safeAreaLayoutGuide.topAnchor, constant: 20),
        stackView.bottomAnchor.constraint(equalTo: innerView.safeAreaLayoutGuide.bottomAnchor, constant: -20),
        shareButton.heightAnchor.constraint(equalToConstant: 30),
        shareButton.widthAnchor.constraint(equalToConstant: 150)
    ])