2
votes

I'm adding a header view to my UITableView and want to add a subview to it having some margins.

class ViewController: UIViewController {
    
    private let tablewView = UITableView()

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white
        
        tablewView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(tablewView)
        [
            tablewView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
            tablewView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor),
            tablewView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
            tablewView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor)
        ].forEach{ $0.isActive = true}

        let headerView = UIView(frame: CGRect(x: 0, y: 120, width: 200, height: 100))
        headerView.backgroundColor = .blue
        
        let subView = UIView(frame: CGRect(x: 10, y: 10, width: 180, height: 80))
        subView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        subView.backgroundColor = .yellow
        headerView.addSubview(subView)
        
        tablewView.tableHeaderView = headerView
    }
}

The problem is that the right margin isn't preserved when the header is resized (when the table view is laid out). As you can see on the image, the right margin is missing:

enter image description here

If I'm using the same view without UITableView, then the margin is preserved as expected.

Is it a UIKit bug? Are there any workarounds?

I know that I can try AutoLayout solutions from here Is it possible to use AutoLayout with UITableView's tableHeaderView? but they're looking a bit hacky. autoresizingMask is supposed to work, after all.

1

1 Answers

1
votes

In Cocoa programming as in comedy, timing is everything.

Add the subview in a one-time implementation of viewDidLayoutSubviews and all will be well. The subview will appear correctly, and will continue working if the table view is resized (e.g. due to rotation of the interface).

So, cut these four lines:

    let subView = UIView(frame: CGRect(x: 10, y: 10, width: 180, height: 80))
    subView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
    subView.backgroundColor = .yellow
    headerView.addSubview(subView)

And instead:

var didLayout = false
override func viewDidLayoutSubviews() {
    guard !didLayout else { return }
    didLayout.toggle()
    if let h = tablewView.tableHeaderView {
        let subView = UIView(frame: h.bounds.insetBy(dx: 10, dy: 10))
        subView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        subView.backgroundColor = .yellow
        h.addSubview(subView)
    }
}