2

1. Empty custom view implementation

I created an empty subclass of UITableViewHeaderFooterView as follows:

class MyCustomHeaderView: UITableViewHeaderFooterView {

}

I registered that class with my table view:

tableView.register(MyCustomHeaderView.self, forHeaderFooterViewReuseIdentifier: "myHeaderView")

and implemented the data source functions as follows:

func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
    let headerView = tableView.dequeueReusableHeaderFooterView(withIdentifier: "myHeaderView")
    return headerView
}

func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
    return "Hello"
}

As expected, this is what I get: A header view that looks just like the system default.

Section Header View with empty class implementation


2. Custom view implementation that overrides drawRect(_:)

Now the reason why I implemented this subclass in the first place is because I want to perform some custom drawing. Thus, I implement the drawRect(_:) method:

class MyCustomHeaderView: UITableViewHeaderFooterView {

    override func draw(_ rect: CGRect) {
        super.draw(rect)
    }

}

Again I do nothing inside this method other than calling the super implementation. So from my point of view this should not change anything because if I don't implement it the super implementation is called anyway.

However, when I now launch the app this is what I get:

Section view header with overridden drawRect(_:) method

The header view's background is completely filled with a solid black color.


Why?


Additional information:

I've done some research and read several posts that recommended to set the header view's isOpaque property to false. I did that and I even set it for the header view's backgroundView and contentView — with no effect.

However, I checked in the Debug View Hierarchy in Xcode that it's actually MyCustomHeaderView itself that's black, not its subviews, and that view is actually opaque — even though I explicitly set it to false in code.

(As you can see from the screenshots I tried this with a brand new project, specifically with the default Xcode master-detail template, to exclude the possibility that my custom table view might be causing this.)

Debug View Hierarchy

Opaque: On

Mischa
  • 15,816
  • 8
  • 59
  • 117
  • I'm trying something similar with similar results. Don't know if anything I tried will help with yours or if mine will become a separate question. Still trying to decipher this. I'm using tableView:willDisplayHeaderView. For custom drawing, I add subviews to the UITableViewHeaderFooterView. What's interesting is that a UIImageView and UILabel draw properly, but a subclassed UIView draws black like for you. Setting background color for UIView DID work...white, clear, etc. However even though the drawRect is called and uses legit drawing, it does not display anything. Dunno why UIView is diff. – JKaz Jun 15 '17 at 19:36
  • This may or may not help with yours. As I experimented, I saw that mine was not drawing be/c of a mismatch between the UIView's frame and bounds. The frame's origin had an x of 150, for example. But the rect for the draw method had x origin of 0. When I set the view's bounds equal to the frame when I created the view, it all worked perfectly. I'm easily confused by these properties, so can't explain why this is different from the behaviors of the image or label. – JKaz Jun 15 '17 at 21:53
  • @Mischa, did you ever find a solution for this? – Stan May 17 '18 at 04:04

1 Answers1

3

When you override a view's drawRect function then you are promising to replace it's normal draw behavior with custom behavior.

The system normally clears the drawing context before calling your drawRect. If you make your view opaque then it erases what's underneath before giving your view a chance to draw. If you don't draw, you wind up with a black box at the view's frame.

I suggest doing a simpler custom view as a starting point. Create a custom subclass of UILabel and add one to a single view app. Note that it draws itself like a normal label unless you implement draw(_rect:) If you implement an empty draw(_rect:), the label isn't drawn any more. Make the view opaque and you get a black box in it's place.

Don't override drawRect unless you are going to provide a custom implementation.

Duncan C
  • 128,072
  • 22
  • 173
  • 272
  • Is there any official documentation that supports this? The docs for `drawRect(_:)` state `The default implementation of this method does nothing.` and I cannot find any indication there that simply implementing that method clears the drawing context. Surely there _must_ be a way to let the system perform its drawing first and then draw something on top of it? How would I do that if not by first calling the `super` implementation and then adding my custom drawing code? – Mischa Apr 20 '17 at 20:50
  • The default implementation might not do anything (though the docs are talking about `UIView`, not `UITableViewHeaderFooterView` which might do something), but the action of overriding the method changes the way view draws its layers. `UIView` has a `clearsContextBeforeDrawing` method which controls whether or not the context gets cleared before `drawRect` is called which defaults to true. – dan Apr 20 '17 at 20:55
  • However, setting `clearsContextBeforeDrawing` to `false` does not change the drawing behavior for the header view. So it can't really be that, right? – Mischa Apr 20 '17 at 21:30
  • I misspoke slightly. It's the opaque flag that causes it to draw black. If you set the view to opaque then it masks whatever is drawn under it. By adding a draw(_ rect:) method you suppress the normal drawing for the view, and if you don't replace it, nothing gets drawn. The result of drawing nothing varies depending on the settings you've selected (opaque or not and clearsGraphicsContextBeforeDrawing or not. – Duncan C Apr 20 '17 at 23:06
  • 1
    Thanks for the clarification and for editing your answer! I did as you suggested and added a subclassed UILabel to a single view app. You are correct that simply overriding the `draw(_:)` method with an _empty_ implementation results in nothing being drawn at all. But this is expected. When I call `super.draw(rect)` from inside the method the label renders exactly as before — again as expected: The super implementation draws the view as it would be drawn without overriding `draw(_:)`. Only that this doesn't work for UITableViewHeaderFooterView subclasses. And that's the essence of my question. – Mischa Apr 21 '17 at 09:37