Starting in iOS7, there is additional space at the top of my UITableView
's which have a style UITableViewStyleGrouped
.
Here is an example:
https://i.stack.imgur.com/yNYTL.png
The tableview starts at the first arrow, there are 35 pixels of unexplained padding, then the green header is a UIView
returned by viewForHeaderInSection
(where the section is 0).
Can anyone explain where this 35-pixel amount is coming from and how I can get rid of it without switching to UITableViewStylePlain
?
Update (Answer):
In iOS 11 and later:
tableView.contentInsetAdjustmentBehavior = .never
UITableView
doesn't like to be assigned a header with a height of 0.0. Check stackoverflow.com/a/31223403/1394534 for more details.
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).
I was helped by the following:
YouStoryboard.storyboard > YouViewController > Attributes inspector > Uncheck - Adjust scroll view insets.
https://i.stack.imgur.com/CUVgX.png
I played around with it a bit more and it seems like this is a side-effect of setting the tableView's tableHeaderView = nil
.
Because my tableView has a dynamically appearing tableHeaderView
, when I need to hide the tableHeaderView
, instead of doing self.tableView.tableHeaderView = nil;
, I do:
self.tableView.tableHeaderView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, self.tableView.bounds.size.width, 0.01f)];
I like this solution better than setting a somewhat arbitrary contentInset.top
because I use the contentInset.top
dynamically as well. Having to remember to remove an extra 35px whenever I recalculate contentInset.top
is tedious.
Debug > Color Misaligned Images
in the simulator to see this for yourself.) You don't want to do that.
UITableViewHeaderFooterView
instead of UIView. And CGFLOAT_MIN
works the same as 0.01f
but better in theory.
Try changing the contentInset
property that UITableView
inherits from UIScrollView
.
self.tableView.contentInset = UIEdgeInsetsMake(-20, 0, 0, 0);
It's a workaround, but it works
contentTableView.contentInset = UIEdgeInsetsMake(-20, 0, -20, 0);
worked best for me because there were 20 extra pixels at the top and bottom.
For IOS 7 if you are allocing a tableview in a view controller you may look into
self.edgesForExtendedLayout = UIRectEdgeNone;
your problem seemed similar to mine
Update:
Swift in iOS 9.x:
self.edgesForExtendedLayout = UIRectEdge.None
Swift 3 :
self.edgesForExtendedLayout = UIRectEdge.init(rawValue: 0)
UITableView
. The issues stems from the fact my UITableView
was on a UIViewController
inside a UINavigationController
which caused the table content to drop down 44 points so initial content wasn't up behind the navBar
. That wasn't needed with my layout though, so it just caused issues. Eventually I changed my code to automaticallyAdjustsScrollViewInsets
which worked also.
edgesForExtendedLayout = []
self.automaticallyAdjustsScrollViewInsets = NO;
try, you can deal with it!
uncheck Adjust scroll view insets
. Because sometimes we don't use storyboard to build our UI.
self.tableView.contentInsetAdjustmentBehavior = .never
You could detect if your app is running iOS7 or greater and add this two methods in your table view delegate (usually in your UIViewController code)
-(CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
return CGFLOAT_MIN;
}
-(CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {
return CGFLOAT_MIN;
}
This maybe is not an elegant solution but works for me
Swift version:
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return CGFloat.leastNormalMagnitude
}
override func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return CGFloat.leastNormalMagnitude
}
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { return section == 0 ? 14 : 24 }
I have found the cause of my original bug and created a sample project showcasing it. I believe there is an iOS7 bug.
As of iOS7, if you create a UITableView with the Grouped style, but do not have a delegate set on first layout, then you set a delegate and call reloadData, there will be a 35px space at the top that will never go away.
See this project I made showcasing the bug: https://github.com/esilverberg/TableViewDelayedDelegateBug
Specifically this file: https://github.com/esilverberg/TableViewDelayedDelegateBug/blob/master/TableViewDelayedDelegateBug/ViewController.m
If line 24 is active,
[self performSelector:@selector(updateDelegate) withObject:nil afterDelay:0.0];
there will be an extra 35 px space at the top. If line 27 is active and 24 is commented out,
self.tableView.delegate = self;
no space at the top. It's like the tableView is caching a result somewhere and not redrawing itself after the delegate is set and reloadData is called.
tableView:heightForHeaderInSection:
and tableView:heightForFooterInSection:
returns 0 you will also have this problem. Implementing the protocol methods above AND returning 0.01f fixed it for me.
Uncheck "Adjust Scroll View insets"
https://i.stack.imgur.com/keeaD.png
Another quick comment... even in XCode 6.1, there is a bug with vertical spaces appearing at the top of UIScrollViews
, UITextViews
and UITableViews
.
https://i.stack.imgur.com/2JLvN.png
Sometimes, the only way to fix this issue is to go into the Storyboard and drag the problem control so it's no longer the first subview on the page.
https://i.stack.imgur.com/qNA76.png
(My thanks to Oded for pointing me in this direction... I'm posting this comment, just to add a few screenshots, to demonstrate the symptoms and fix.)
Solution for iOS 15:
if #available(iOS 15.0, *) {
tableView.sectionHeaderTopPadding = 0
}
To fix in a whole project:
if #available(iOS 15.0, *) {
UITableView.appearance().sectionHeaderTopPadding = 0
}
More details: Extra padding above table view headers in iOS 15
Note: This only applies to UITableView.Style.plain
.
While using grouped TableView use this to avoid border cutting in viewWillAppear
self.tableView.contentInset = UIEdgeInsetsMake(-35, 0, 0, 0);
According to this transition guide for iOS7 by Apple, the scroll view’s content insets is automatically adjusted. The default value of automaticallyAdjustsScrollViewInsets is set to YES.
The UIViewController which has the UITableView should set this property to NO.
self.automaticallyAdjustsScrollViewInsets = NO;
This will do the trick.
EDIT 1:
Also, one could try -
self.navigationController.navigationBar.translucent = YES;
This also removes the extra padding on the top.
A lot of the previous answers above are too hacky. They would break at anytime in the future if Apple decides to fix this unexpected behavior.
Root of the issue:
a UITableView doesn't like to have a header with a height of 0.0. If what's you're trying to do is to have a header with a height of 0, you can jump to the solution. even if later you assign a non 0.0 height to your header, a UITableView doesn't like to be assigned a header with a height of 0.0 at first.
Solution:
Then, the most simple and reliable fix is to ensure that your header height is not 0 when you assign it to your table view.
Something like this would work:
// Replace UIView with whatever class you're using as your header below:
UIView *tableViewHeaderView = [[UIView alloc] initWithFrame:CGRectMake(0.0, 0.0, self.tableView.bounds.size.width, CGFLOAT_MIN)];
self.tableView.tableHeaderView = tableViewHeaderView;
Something like this would lead to the issue at some point (typically, after a scroll):
// Replace UIView with whatever class you're using as your header below:
UIView *tableViewHeaderView = [[UIView alloc] initWithFrame:CGRectZero];
self.tableView.tableHeaderView = tableViewHeaderView;
tableView.tableHeaderView
is actually an accessory view above row content. I was confused with section headers all the way until your answer came out.
CGFLOAT_MIN
has been replaced with CGFloat.leastNormalMagnitude
in Swift 3
Storyboard:
Just uncheck: Adjust Scroll View Insets
in View Controller's options
https://i.stack.imgur.com/J4C7u.png
Code:
self.automaticallyAdjustsScrollViewInsets = false
This is the solution for iOS 10 using Swift 3:
You can get rid of top and bottom paddings by implementing the following methods from the UITableViewDelegate
.
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat
{
return CGFloat.leastNormalMagnitude
}
func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat
{
return CGFloat.leastNormalMagnitude
}
This code worked for me, The best answer for me that was written in objective-C
at up-side so I converted it into Swift.
For Swift 4.0+
self.tableView.tableHeaderView = UIView(frame: CGRect(x: 0, y: 0, width: self.tableView.bounds.size.width, height: .leastNonzeroMagnitude))
Just write this into viewDidLoad()
and it will work like a charm.
For iOS 15+, above one won't work, so use this:-
if #available(iOS 15.0, *) {
tableView.sectionHeaderTopPadding = 0
}
For iOS 15+, if you want to apply change for your whole project, so use this:-
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
if #available(iOS 15.0, *) {
UITableView.appearance().sectionHeaderTopPadding = 0.0
}
}
So I was trying every method here, and this time none of them helped. My case was a grouped table view on iOS 9. I don't really know why and how I found out this one, but for me, setting the tableViewHeader
with a UIView
with at least 0.01
height worked out. CGRectZero
didn't help, nothing really helped:
tableView.tableHeaderView = UIView(frame: CGRect(x: 0.0, y: 0.0, width: 0.0, height: 0.01))
Simply add the following to your viewDidLoad in your VC:
self.automaticallyAdjustsScrollViewInsets = NO;
In my case this was what helped me. I'm supporting ios6 also.
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7) {
self.edgesForExtendedLayout = UIRectEdgeNone;
self.extendedLayoutIncludesOpaqueBars = NO;
self.automaticallyAdjustsScrollViewInsets = NO;
}
This is how it can be fixed easily in iOS 11 and Xcode 9.1 through Storyboard:
Select Table View > Size Inspector > Content Insets: Never
automaticallyAdjustsScrollViewInsets
in ios 11
Swift: iOS I had tableview on scroll view .. when I was click "Back" on the same screen. Scroll view take more space on top.. to solve this I have used :
self.automaticallyAdjustsScrollViewInsets = false
A Boolean value that indicates whether the view controller should automatically adjust its scroll view insets. Default value is true, which allows the view controller to adjust its scroll view insets in response to the screen areas consumed by the status bar, navigation bar, and toolbar or tab bar. Set to false if you want to manage scroll view inset adjustments yourself, such as when there is more than one scroll view in the view hierarchy.
Thanks to the answer by @Aurelien Porte. Here is my solution
Cause of this issue:-
a UITableView doesn't like to have a header with a height of 0.0. If what's you're trying to do is to have a header with a height of 0, you can jump to the solution. even if later you assign a non 0.0 height to your header, a UITableView doesn't like to be assigned a header with a height of 0.0 at first.
In ViewDidLoad:-
self.edgesForExtendedLayout = UIRectEdge.None
self.automaticallyAdjustsScrollViewInsets = false
No Need For Something Like This :-
self.myTableview.contentInset = UIEdgeInsetsMake(-56, 0, 0, 0)
In heightForHeaderInSection
delegate:-
if section == 0
{
return 1
}
else
{
return 40; // your other headers height value
}
In viewForHeaderInSection
delegate :-
if section == 0
{
// Note CGFloat.min for swift
// For Objective-c CGFLOAT_MIN
let headerView = UIView.init(frame: CGRectMake(0.0, 0.0, self.myShaadiTableview.bounds.size.width, CGFloat.min))
return headerView
}
else
{
// Construct your other headers here
}
To be specific, to remove tableviewHeader space from top i made these changes:
YouStoryboard.storyboard > YouViewController > Select TableView > Size inspector > Content insets - Set it to never.
https://i.stack.imgur.com/j9n14.png
I'm assuming that is just part of the new UITableViewStyleGrouped
styling. It is in all grouped table views and there doesn't seem to be any direct way to control that space.
If that space is being represented by a UIView
, it would be possible to search through all the subviews
of the UITableView
to find that specific view and edit it directly. However, there is also the possibility that that space is just a hardcoded offset before headers and cells start and there won't be any way to edit it.
To search through all subviews (I would run this code when the table has no cells, to make it a little easier to read the output):
- (void)listSubviewsOfView:(UIView *)view {
// Get the subviews of the view
NSArray *subviews = [view subviews];
// Return if there are no subviews
if ([subviews count] == 0) return;
for (UIView *subview in subviews) {
NSLog(@"%@", subview);
// List the subviews of subview
[self listSubviewsOfView:subview];
}
}
UITableViewCellScrollView
) on iOS7 have taught us one thing, it's to leave the view hierarchy of built in classes alone.
po [((UIApplication *)UIApplication.sharedApplication).keyWindow recursiveDescription]
you'll see that the table header, or first section header, or first cell (depending on what you use) naturally leaves a 35px border.... boo.
My answer is going to be more general answer, but can be applied on this as well.
If the root view (of the ViewController) or the first child (subview) of the root view is subclass of the UIScrollView (or UIScrollView itself), and if
self.navigationController.navigationBar.translucent = YES;
framework will automatically set pre-calculated contentInset.
To avoid this you can do
self.automaticallyAdjustsScrollViewInsets = NO;
but in my case I wasn't able to do this, because I was implementing SDK which has UIView component which can be used by other developers. That UIView component contains UIWebView (which has UIScrollView as the first subview). If that component is added as the first child in the UIViewController's view hierarchy, automatic insets will be applied by system.
I've fixed this by adding dummy view with frame (0,0,0,0) before adding UIWebView.
In this case system didn't find subclass of the UIScrollView as the first subview and didn't apply insets
self.automaticallyAdjustsScrollViewInsets = false
The only thing that worked for me was:
Swift:
tableView.sectionHeaderHeight = 0
tableView.sectionFooterHeight = 0
Objective-C:
self.tableView.sectionHeaderHeight = 0;
self.tableView.sectionFooterHeight = 0;
Also, I still had an extra space for the first section. That was because I was using the tableHeaderView
property incorrectly. Fixed that as well by adding:
self.tableView.tableHeaderView = UIView(frame: CGRect(x: 0, y: 0, width: tableView.frame.size.width, height: 0.01))
Swift 4 code: For tableview with no section headers you can add this code:
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return CGFloat.leastNormalMagnitude
}
and you will get the header spacing to 0.
If you want a header of your specific height pass that value:
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return header_height
}
and the view from viewForHeaderinSection delegate.
2019 answer:
You just do this
tableView.contentInsetAdjustmentBehavior = .never
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()
}
I had the same fix as arielyz. Once I moved the UITableView to be not the first subview of the parent view, it went away. My space was 20 px, not 35.
I wasn't able to recreate it in a portrait xib, only a landscape xib. I'll file a radar bug later if I can reproduce it in a simple demo app.
I think making UIEdgeInsets -35 0 0 0 is tedious. In my case, I implemented tableView: heightForHeaderInSection: method and it has a potential to return 0.
When I changed 0 to 0.1f, the problem just went away.
Success story sharing
collectionview
. (When tableview is insidecollectionviewcell
)