Auto resize the UIView based on UILabel's content through Auto Layout

Viewed 3425

I have a requirement on showing a banner view to show a message,

Now the message's content may vary and the view should also resize depending on UILabels content.

UILabel is set to Word Wrap and numberOfLines is set to 0

The design in xib is, Auto layoutAuto Layout 2

And the respective class file is,

import UIKit

class ORABannerView: UIView {

    @IBOutlet weak var bannerText: UILabel!

    static func instantiate(message: String) -> ORABannerView {
        let view: ORABannerView = initFromNib() ?? ORABannerView()
        view.bannerText.text = message
        return view
    }

    override init(frame: CGRect) {
        super.init(frame: frame)
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }

    override func draw(_ rect: CGRect) {
        super.draw(rect)
    }
}

The initFromNib is an implemented as UIView's extension,

extension UIView {
    class func initFromNib<T: UIView>() -> T? {
        guard let view = Bundle.main.loadNibNamed(String(describing: self), owner: nil, options: nil)?[0] as? T else {
            return nil
        }
        return view
    }
}

Tried with layoutIfNeeded() on the view, but it's not working for me.

Any suggestions will be appreciated.

4 Answers

You may want to try the following code,

Create a method sizeHeaderToFit and which returns height of the UIView based on its content's height.

private func sizeHeaderToFit(headerView: UIView?) -> CGFloat {
       guard let headerView = headerView else {
           return 0.0
       }

       headerView.setNeedsLayout()
       headerView.layoutIfNeeded()

       let height = headerView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize).height

       return height
   }

Then you can call the above method from the respective code,

sizeHeaderToFit(headerView: yourView)

In ORABannerView class, add override layoutSubviews,

override func layoutSubviews() {
       super.layoutSubviews()
       bannerText.preferredMaxLayoutWidth = bannerText.bounds.width
   }

And in xib file, keep the constraints to its superview rather than safe area for code optimisation. [Optional]

Hope it helps.

  1. Remove trailing OR leading constraints
  2. set the size to fit property programmatically

I expect the class to look like

class ORABannerView: UIView {

    @IBOutlet weak var bannerText: UILabel!

    static func instantiate(message: String) -> ORABannerView {
        let view: ORABannerView = initFromNib()
        view.bannerText.text = message
        return view
    }

    override init(frame: CGRect) {
        super.init(frame: frame)
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }

    override func draw(_ rect: CGRect) {
        super.draw(rect)
    }
}

let v = ORABannerView.instantiate(message: "djhhejjhsdjhdjhjhdshdsjhdshjdhjdhjdhjddhjhjdshjdhdjshjshjdshjdshjdshjdshjdsjhdshjds")

self.view.addSubview(v)

v.translatesAutoresizingMaskIntoConstraints = false

NSLayoutConstraint.activate([

    v.topAnchor.constraint(equalTo: self.view.topAnchor,constant:50),

    v.leadingAnchor.constraint(equalTo: self.view.leadingAnchor,constant:0),

    v.trailingAnchor.constraint(equalTo: self.view.trailingAnchor,constant:0)

])

enter image description here

New users autolayout, always forgot about two useful constraints:

func setContentCompressionResistancePriority(UILayoutPriority, for: NSLayoutConstraint.Axis)
func setContentHuggingPriority(UILayoutPriority, for: NSLayoutConstraint.Axis)

It also exists in IB too: enter image description here

Try to play with the priority parameter

Related