Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I create UITableView header whose height is determined by the height of its label?

Tags:

ios

swift

I would like to add a header to my tableView. This header contains 1 UILabel. The header height should be calculated based on the number of lines the label has.

In my code, I'm adding constraints with all the edges of the label <> header. This is my attempt:

    //Add header to tableView
    header = UIView()
    header.backgroundColor = UIColor.yellowColor()
    tableView!.tableHeaderView = header

    //Create Label and add it to the header
    postBody = UILabel()
    postBody.text = "The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog."
    postBody.font = UIFont(name: "Lato-Regular", size: 16.0)
    postBody.numberOfLines = 0
    postBody.backgroundColor = FlatLime()
    header.addSubview(postBody)

    //Enable constraints for each item
    postBody.translatesAutoresizingMaskIntoConstraints = false
    header.translatesAutoresizingMaskIntoConstraints = false

    //Add constraints to the header and post body
    let postBodyLeadingConstraint = NSLayoutConstraint(item: postBody, attribute: NSLayoutAttribute.Leading, relatedBy: NSLayoutRelation.Equal, toItem: header, attribute: NSLayoutAttribute.Leading, multiplier: 1, constant: 0)
    postBodyLeadingConstraint.active = true

    let postBodyTrailingConstraint = NSLayoutConstraint(item: postBody, attribute: NSLayoutAttribute.Trailing, relatedBy: NSLayoutRelation.Equal, toItem: header, attribute: NSLayoutAttribute.Trailing, multiplier: 1, constant: 0)
    postBodyTrailingConstraint.active = true


    let postBodyTopConstraint = NSLayoutConstraint(item: postBody, attribute: NSLayoutAttribute.Top, relatedBy: NSLayoutRelation.Equal, toItem: header, attribute: NSLayoutAttribute.Top, multiplier: 1, constant: 0)
    postBodyTopConstraint.active = true


    let postBodyBottomConstraint = NSLayoutConstraint(item: postBody, attribute: NSLayoutAttribute.Bottom, relatedBy: NSLayoutRelation.Equal, toItem: header, attribute: NSLayoutAttribute.Bottom, multiplier: 1, constant: 0)
    postBodyBottomConstraint.active = true


    //Calculate header size
    let size = header.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize)
    var frame = header.frame
    frame.size.height = size.height
    header.frame = frame
    tableView!.tableHeaderView = header
    header.layoutIfNeeded()

This is my table:

    let nib = UINib(nibName: "MessagesTableViewCell", bundle: nil)
    let nibSimple = UINib(nibName: "SimpleMessagesTableViewCell", bundle: nil)
    self.tableView!.registerNib(nib, forCellReuseIdentifier: "MessagesTableViewCell")
    self.tableView!.registerNib(nibSimple, forCellReuseIdentifier: "SimpleMessagesTableViewCell")
    self.tableView!.dataSource = self
    self.tableView!.delegate = self
    self.tableView!.rowHeight = UITableViewAutomaticDimension
    self.tableView!.estimatedRowHeight = 100.0
    self.tableView!.separatorStyle = UITableViewCellSeparatorStyle.None
    self.tableView!.separatorColor = UIColor(hex: 0xf5f5f5)
    self.tableView!.separatorInset = UIEdgeInsetsMake(0, 0, 0, 0)
    self.tableView!.clipsToBounds = true
    self.tableView!.allowsSelection = false
    self.tableView!.allowsMultipleSelection = false
    self.tableView!.keyboardDismissMode = .OnDrag

As you can see, the header does not take into account the height of the label (which I did numberOfLines = 0)

Header does not auto-height

like image 443
TIMEX Avatar asked Apr 26 '16 06:04

TIMEX


People also ask

How do I scroll the header along with UITableView?

If you want to have table view header along with section headers you have to set UITableViewStyle property to UITableViewStyleGrouped . Show activity on this post. If you have a single header in the table then you can use tableHeaderView as below: tableView.

What is header in Swift?

For output message, the basic header identifies the application through which SWIFT has processed the message. The basic header also identifies the type of output data, the receiving logical terminal, and (if required) the output session number and output sequence number.


2 Answers

UILabels take advantage of UIView's intrinsicContentSize() to tell auto layout what size they should be. For a multiline label, however, the intrinsic content size is ambiguous; the table doesn't know if it should be short and wide, tall and narrow, or anything in between.

To combat this, UILabel has a property called preferredMaxLayoutWidth. Setting this tells a multiline label that it should be at most this wide, and allows intrinsicContentSize() to figure out and return an appropriate height to match. By not setting the preferredMaxLayoutWidth in your example, the label leaves its width unbounded and therefore calculates the height for a long, single line of text.

The only complication with preferredMaxLayoutWidth is that you typically don't know what width you want the label to be until auto layout has calculated one for you. For that reason, the place to set it in a view controller subclass (which it looks like your code sample might be from) is in viewDidLayoutSubviews:

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    postBody.preferredMaxLayoutWidth = CGRectGetWidth(postBody.frame)
    // then update the table header view
    if let header = tableView?.tableHeaderView {
        header.frame.size.height = header.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize).height
        tableView?.tableHeaderView = header
    }
}

Obviously, you'll need to add a property for the postBody label for this to work.

Let me know if you're not in a UIViewController subclass here and I'll edit my answer.

like image 92
stefandouganhyde Avatar answered Nov 15 '22 21:11

stefandouganhyde


Implementation using the storyboard

  1. In UItableView add on UITableViewCell new UIView and put him UILabel Connects them via Autolayout
  2. In UILabel put the number of lines to 0.
  3. In ViewDidLoad your UILabel call a method sizeToFit() and specify a size for UIView, and that will be your HeaderVew headerView.frame.size.height = headerLabel.frame.size.height

Code

    @IBOutlet weak var tableView: UITableView!
    @IBOutlet weak var headerView: UIView!
    @IBOutlet weak var headerLabel: UILabel!

    override func viewDidLoad() {
        super.viewDidLoad()

        headerLabel.text = "tableViewdidReceiveMemoryWarningdidReceiveMemoryWarningdidReceiveMemoryWarningdidReceiveMemoryWarningdidReceiveMemoryWarningdidReceiveMemoryWarningdidReceiveMemoryWarningdidReceiveMemoryWarningdidReceiveMemoryWarning"
        headerLabel.sizeToFit()
        headerView.frame.size.height = headerLabel.frame.size.height
    }

ScreenShot

enter image description here

TestProject

test project link

like image 30
Beslan Tularov Avatar answered Nov 15 '22 21:11

Beslan Tularov