Eureka: Globally format section headers - Autosizing headers

Created on 11 Dec 2015  路  1Comment  路  Source: xmartlabs/Eureka

I realize a number of questions have been asked surrounding this topic. But I am wondering what the preferred way of globally within an app changing the display or format of a section header. Can a subclass of Section be made effectively?

I have used the custom UIView (.Class) option of the HeaderFooterView but am not sure at this point how I can set up internal views of this custom view that are customizable... like section title, etc. Is there a way to do this within the onSetupView callback?

Any examples would be super helpful. Thanks

section header footer question

Most helpful comment

It's up to you to subclass Section class.

Here is an example of autosized header text....

class HomeViewController : FormViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        tableView?.estimatedSectionHeaderHeight = 40 // without this line the autosize doesn't work.
        form +++=

            MySection("This is  a very very very long title, This is  a very very very long title, This is  a very very very long title, This is  a very very very long title, This is  a very very very long title, This is  a very very very long title") { _ in }
                    <<< ButtonRow() { (row: ButtonRow) -> Void in
                       row.title = "About"
                    }  .onCellSelection({ [weak self] (cell, row) in
                        let mySection = row.section?.form?[0] as? MySection
                        mySection?.myHeaderString =  mySection?.myHeaderString == "aaaaaaaaa" ? "This is  a very very very long title, This is  a very very very long title, This is  a very very very long title, This is  a very very very long title, This is  a very very very long title, This is  a very very very long title": "aaaaaaaaa"   
                        self?.tableView?.beginUpdates()
                        self?.tableView?.endUpdates()
                    })
    }

    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        tableView?.beginUpdates()
        tableView?.endUpdates()
    }

    class MySection: Section {

        var myHeaderString: String? {
            didSet {
                let view = header?.viewForSection(self, type: .Header)
                view?.setNeedsLayout()
            }
        }

        override init(_ header: String, @noescape _ initializer: Section -> ()) {
            myHeaderString = header
            super.init(header, initializer)
            var header = HeaderFooterView<AutoSizeLabelView>(.Class)
            header.height = { UITableViewAutomaticDimension }
            header.onSetupView = { v, s in
                v.label.text = (s as? MySection)?.myHeaderString
            }
            self.header = header

        }

        required init(@noescape _ initializer: Section -> ()) {
            var header = HeaderFooterView<AutoSizeLabelView>(.Class)
            header.height = { UITableViewAutomaticDimension }
            header.onSetupView = { v, s in
                v.label.text = (s as? MySection)?.myHeaderString
            }

            super.init(initializer)
        }

        required init() {
            var header = HeaderFooterView<AutoSizeLabelView>(.Class)
            header.height = { UITableViewAutomaticDimension }
            header.onSetupView = { v, s in
                v.label.text = (s as? MySection)?.myHeaderString
            }

            super.init()
        }

    }

}

class AutoSizeLabelView: UIView {

    lazy var label: UILabel = {
        var result = UILabel()
        result.translatesAutoresizingMaskIntoConstraints = false
        result.numberOfLines = 0
        return result
    }()

    override init(frame: CGRect) {
        super.init(frame: frame)
        addSubview(label)
        addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-[label]-|", options: NSLayoutFormatOptions.AlignAllBaseline, metrics: nil, views: ["label": label]))
        addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-[label]-|", options: NSLayoutFormatOptions.AlignAllLeft, metrics: nil, views: ["label": label]))
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func layoutSubviews() {
        label.preferredMaxLayoutWidth = frame.size.width
        superview?.setNeedsLayout()
    }
}

Here is the result...

example

Ideally for autosized headers it would be better to use a nib file to define the header view and its constraints.
tableView?.reloadData() can be used instead of beginUpdate() and endUpdate() in case you are not interested in animating the transition.

onSetupView is called each time the header/footer appears on the screen, I mean whenever any of these table view delegate methods are invoked...

public func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView?
public func tableView(tableView: UITableView, viewForFooterInSection section: Int) -> UIView?

Based on that, onSetupView must not be used to change header/footer subviews hierarchy, it's a good place to set up view properties such as fonts, colors, text, etc.

>All comments

It's up to you to subclass Section class.

Here is an example of autosized header text....

class HomeViewController : FormViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        tableView?.estimatedSectionHeaderHeight = 40 // without this line the autosize doesn't work.
        form +++=

            MySection("This is  a very very very long title, This is  a very very very long title, This is  a very very very long title, This is  a very very very long title, This is  a very very very long title, This is  a very very very long title") { _ in }
                    <<< ButtonRow() { (row: ButtonRow) -> Void in
                       row.title = "About"
                    }  .onCellSelection({ [weak self] (cell, row) in
                        let mySection = row.section?.form?[0] as? MySection
                        mySection?.myHeaderString =  mySection?.myHeaderString == "aaaaaaaaa" ? "This is  a very very very long title, This is  a very very very long title, This is  a very very very long title, This is  a very very very long title, This is  a very very very long title, This is  a very very very long title": "aaaaaaaaa"   
                        self?.tableView?.beginUpdates()
                        self?.tableView?.endUpdates()
                    })
    }

    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        tableView?.beginUpdates()
        tableView?.endUpdates()
    }

    class MySection: Section {

        var myHeaderString: String? {
            didSet {
                let view = header?.viewForSection(self, type: .Header)
                view?.setNeedsLayout()
            }
        }

        override init(_ header: String, @noescape _ initializer: Section -> ()) {
            myHeaderString = header
            super.init(header, initializer)
            var header = HeaderFooterView<AutoSizeLabelView>(.Class)
            header.height = { UITableViewAutomaticDimension }
            header.onSetupView = { v, s in
                v.label.text = (s as? MySection)?.myHeaderString
            }
            self.header = header

        }

        required init(@noescape _ initializer: Section -> ()) {
            var header = HeaderFooterView<AutoSizeLabelView>(.Class)
            header.height = { UITableViewAutomaticDimension }
            header.onSetupView = { v, s in
                v.label.text = (s as? MySection)?.myHeaderString
            }

            super.init(initializer)
        }

        required init() {
            var header = HeaderFooterView<AutoSizeLabelView>(.Class)
            header.height = { UITableViewAutomaticDimension }
            header.onSetupView = { v, s in
                v.label.text = (s as? MySection)?.myHeaderString
            }

            super.init()
        }

    }

}

class AutoSizeLabelView: UIView {

    lazy var label: UILabel = {
        var result = UILabel()
        result.translatesAutoresizingMaskIntoConstraints = false
        result.numberOfLines = 0
        return result
    }()

    override init(frame: CGRect) {
        super.init(frame: frame)
        addSubview(label)
        addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-[label]-|", options: NSLayoutFormatOptions.AlignAllBaseline, metrics: nil, views: ["label": label]))
        addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-[label]-|", options: NSLayoutFormatOptions.AlignAllLeft, metrics: nil, views: ["label": label]))
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func layoutSubviews() {
        label.preferredMaxLayoutWidth = frame.size.width
        superview?.setNeedsLayout()
    }
}

Here is the result...

example

Ideally for autosized headers it would be better to use a nib file to define the header view and its constraints.
tableView?.reloadData() can be used instead of beginUpdate() and endUpdate() in case you are not interested in animating the transition.

onSetupView is called each time the header/footer appears on the screen, I mean whenever any of these table view delegate methods are invoked...

public func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView?
public func tableView(tableView: UITableView, viewForFooterInSection section: Int) -> UIView?

Based on that, onSetupView must not be used to change header/footer subviews hierarchy, it's a good place to set up view properties such as fonts, colors, text, etc.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

JonathanImperato picture JonathanImperato  路  3Comments

fpena picture fpena  路  3Comments

Tomas1405 picture Tomas1405  路  3Comments

calli23 picture calli23  路  3Comments

s1rc picture s1rc  路  3Comments