AppsDev
AppsDev

Reputation: 12499

How to properly set a dynamic header view in table view?

I need the header view for a section to be a UIImageView and a UILabel below it. The height for the image view is not going to change once created, but the text in the label may change as a result of some user actions. I need to update the height for the whole header view dynamically (using AutoLayout, not changing the frame).

I've been checking some posts, for example this one, but solutions I tried are not working for me. The height of my header view is not updated when I change the text in the label.

Maybe I need to understand how does this work from the beginning. Firstly, I'd like to be clear about this:

  1. What is the difference between providing a header view as a subclass of UIView in tableView(_:viewForHeaderInSection:), and providing it as a subclass of UITableViewHeaderFooterView and registering it to the table view?

  2. Which constraints do the subviews in the header view need to be able to have a dynamic height?

  3. How should I dynamically update the height of the header view?

Upvotes: 2

Views: 1093

Answers (2)

Milan Nosáľ
Milan Nosáľ

Reputation: 19737

I believe this is pretty simple:

  1. Setup tableView:

    tableView.estimatedSectionHeaderHeight = 63
    tableView.sectionHeaderHeight = UITableViewAutomaticDimension
    
  2. Implement your custom section header and return it in the delegate method:

    override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
    
        switch section {
        case 0:
            return sectionHeader
    
        default:
            fatalError("Unreachable code")
        }
    }
    
  3. Finally, if the contents in the header section changes while the header is presented, after the change you will have to tell the tableView to redraw itself using:

    // method in the tableViewController
    func refreshTableAfterCellExpansion() {
        self.tableView.beginUpdates()
        self.tableView.setNeedsLayout()
        self.tableView.endUpdates()
    }
    

Upvotes: 0

Daniel Dramond
Daniel Dramond

Reputation: 1598

You may want to use a function like the one below. Tweak the attributes to fit the font and font size you have used for the label:

func handleEstimatedFrameForText(_ text: String) -> CGRect {
    let size = CGSize(width: 200, height: 1000)
    let options = NSStringDrawingOptions.usesFontLeading.union(.usesLineFragmentOrigin)
    return NSString(string: text).boundingRect(with: size, options: options, attributes: [NSAttributedStringKey.font: UIFont(name: "AvenirNext-Medium", size: 16) as Any], context: nil)
}

When you use the method to set the size of your header, you want to use this function to get the height of the UILabel AFTER the text has been added in and then add on and kind of padding, UIImageView height etc.

Inside your method that sets the header size

 var height: CGFloat = 0
    let headerMessage = messages[indexPath.item]
    if let labelText = headerMessage.text {
        let headerImageViewHeight: CGFloat = 80 /* ADD IN YOUR UIIMAGEVIEW HEIGHT */
        height = self.handleEstimatedFrameForText(labelText).height + headerImageViewHeight
        return CGSize(width: view.frame.width, height: height)
    } else {
        return CGSize(width: view.frame.width, height: 200) /* DEFAULT SIZE */
    }

You may want to add an extra few pixels to the self.handleEstimatedFrameForText(labelText).height + headerImageViewHeight as some fonts need an extra 12 pixels or so the work on all devices. Trial and error.

Upvotes: 0

Related Questions