2

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?

rmaddy
  • 314,917
  • 42
  • 532
  • 579
AppsDev
  • 12,319
  • 23
  • 93
  • 186

2 Answers2

0

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()
    }
    
Milan Nosáľ
  • 19,169
  • 4
  • 55
  • 90
  • Thanks, what the method `tableView(_:heightForHeaderInSection:)` should return then? I'm seeing that it is called before `tableView(_:viewForHeaderInSection:)` – AppsDev Jan 22 '18 at 03:29
  • @AppsDev it should be called only when it is implemented, so either remove it altogether, or return `UITableViewAutomaticDimension`. I personally prefer to simply not implement it (that method should be optional in `UITableViewDelegate`, so you don't have to implement it) – Milan Nosáľ Jan 22 '18 at 10:09
0

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.

Daniel Dramond
  • 1,538
  • 2
  • 15
  • 26
  • Thanks, but this is what I was trying to avoid, having to "manually" calculate the height of the header... is it not possible to simply rely on AutoLayout to get it there? – AppsDev Jan 22 '18 at 03:22
  • @AppsDev Auto Layout will do it’s job inside of your cell by making the UILabels height determine on the content within it but unfortunately you have to manually set the tableViews header or row height no matter what – Daniel Dramond Jan 22 '18 at 10:56