2022 answer:
You just do this
tableView.contentInsetAdjustmentBehavior = .never
which is undocumented
Bizarre subtle gotchya ->
Tableviews have a very strange behavior these days:
On devices with a notch (XR, etc) it will without telling you add more inset BUT ONLY IF the table starts at the physical top of the screen.
If you start NOT at the top of the screen, it won't do that, but
Both of those cases are >> unrelated << to safeAreaInsets
....... which is very confusing
All of that is totally undocumented ... you can waste hours figuring this out.
If you do need your measurements to start actually from the top of the screen/table,
in fact simply go:
tableView.contentInsetAdjustmentBehavior = .never
A good example is obviously when you add some sort of banner or similar thing over the top of a table, which is common these days, and you just set the top inset of the table to whatever height your banner/etc becomes when it's running.
To do that, you must use the
tableView.contentInsetAdjustmentBehavior = .never
call :/
Bonus gotchya
Don't forget that almost always these days, you're loading some information (user pictures, description, whatever) dynamically, so you can't set such values to the final needed value until the info arrives. Another gotchya. :/
So you'd have code like:
func setTableOffsetOnceFlagAreaSizeIsKnown() {
tableView.contentInset.top = yourSpecialFlagViewUpTop.bounds.height
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
setTableOffsetOnceFlagAreaSizeIsKnown()
}
self.tableView.tableHeaderView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 0.0f, CGFLOAT_MIN)];
note:0.0f
is just ignored if you use it in the height of the rect. So we use the nearest-to-zero CGFloat possible (at least this "worked" for me... just not ideal solution).