iOS 7 Table view fail to auto adjust content inset
Solution 1
I have found the answer on apple developer forum. There are two different case.
The first one, the view controller added is a UITableViewController. And the issue should not be appeared since apple will auto padding it.
The second one, the view controller is NOT a UITableViewController. And in the view hierarchy, it contains a UITableView. In this case, if the UITableview(or ScrollView) is the viewController's mainview or the first subview of the mainview, it will work. Otherwise, the view controller doesn't know which scroll view to padding and it will happen the issue.
In my case, the view controller is the second one. And there is a background image view as the first subview of the main view. So, it fails.
Here is the Apple developer forum link (need developer account to access): https://devforums.apple.com/message/900138#900138
Solution 2
If you want the view to underlap the navigation bar, but also want it positioned so the top of the scrollview's content is positioned below the navigation bar by default, you can add a top inset manually once the view is laid out. This is essentially what the view layout system does when the top-level view is a scroll view.
-(void)viewDidLayoutSubviews {
if ([self respondsToSelector:@selector(topLayoutGuide)]) {
UIEdgeInsets currentInsets = self.scrollView.contentInset;
self.scrollView.contentInset = (UIEdgeInsets){
.top = self.topLayoutGuide.length,
.bottom = currentInsets.bottom,
.left = currentInsets.left,
.right = currentInsets.right
};
}
}
Solution 3
Based on Tony's answer I was able to get around this problem programatically with temporarily sending the table view to the back, let the adjustments be made and then send the background view back to the back. In my case there is no flickering to this approach.
In the View Controller:
- (void)viewWillLayoutSubviews {
[super viewWillLayoutSubviews];
[self.view sendSubviewToBack:self.tableView];
}
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
[self.view sendSubviewToBack:self.backgroundView];
}
Obviously if there are other subviews on self.view
you may need to re-order those too.
Solution 4
There's probably too many answers on this already, but I had to take Christopher's solution and modify it slightly to support view resizing and allowing the content inset to be changed in a subclass of the UIViewController
.
@interface MyViewController ()
@property (weak, nonatomic) IBOutlet UIScrollView *scrollView;
@property (assign, nonatomic) UIEdgeInsets scrollViewInitialContentInset;
@end
@implementation MyViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self setScrollViewInitialContentInset:UIEdgeInsetsZero];
}
- (void)viewWillLayoutSubviews {
[super viewWillLayoutSubviews];
if (UIEdgeInsetsEqualToEdgeInsets([self scrollViewInitialContentInset], UIEdgeInsetsZero)) {
[self setScrollViewInitialContentInset:[self.scrollView contentInset]];
}
}
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
UIEdgeInsets scrollViewInset = [self scrollViewInitialContentInset];
if (UIEdgeInsetsEqualToEdgeInsets(scrollViewInset, UIEdgeInsetsZero) {
if ([self respondsToSelector:@selector(topLayoutGuide)]) {
scrollViewInset.top = [self.topLayoutGuide length];
}
if ([self respondsToSelector:@selector(bottomLayoutGuide)]) {
scrollViewInset.bottom = [self.bottomLayoutGuide length];
}
[self.scrollView setContentInset:scrollViewInset];
}
}
@end
To explain the point:
Any subclass of MyViewController
can now modify the contentInset
of scrollView
in viewDidLoad
and it will be respected. However, if the contentInset
of scrollView
is UIEdgeInsetsZero
: it will be expanded to topLayoutGuide
and bottomLayoutGuide
.
Solution 5
@Christopher Pickslay solution in Swift 2:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
let topInset = topLayoutGuide.length
inTableView.contentInset.top = topInset
inTableView.contentOffset.y = -topInset
inTableView.scrollIndicatorInsets.top = topInset
}
Comments
-
Tony Fung almost 4 years
I am transiting my project to iOS7. I am facing a strange problem related to the translucent navigation bar.
I have a view controller and it has a tableview as subview (let's call it ControllerA) . I init a new uinavigationcontroller with the controllerA and present it modally using presentviewcontroller. The presented view controller's table view is blocked by the navigation bar. I set the automaticallyAdjustsScrollViewInsets to YES but the result did not change. I knew I can set the edgesForExtendedLayout to UIRectEdgeNone, but it will make the navigation bar no more translucent.
After that, I tried to create a new view controller for testing. It contains almost the same elements. But the result is much different. The table view content does not get blocked.
Conclusion
- Two View Controllers' automaticallyAdjustsScrollViewInsets set to YES
- The project is not using storyboard
- The first one is created at Xcode 4.6, The second one is newly created on Xcode 5
- I have compared two classes xib and code, not much different
-
Tony Fung over 10 yearsDid you try to set the self.edgesForExtendedLayout = UIRectEdgeNone; It can disable the content inset auto padding.
-
DaGaMs over 10 yearsThanks, this was useful for my particular case: I have a
UICollectionViewController
inside aUIPageViewController
, the latter of which is pushed onto aUINavigationController
. The internal scrollView of the pageViewController preventsautomaticallyAdjustsScrollviewInsets
from working correctly. Unfortunately, thetopLayoutGuide
is not set correctly,either, but I can still manually get the offset of the navigation bar:CGFloat topOffset = self.navigationController.navigationBar.frame.origin.y + self.navigationController.navigationBar.frame.size.height
-
Redwarp about 10 yearsI have the same issue, the scrollview is the second subview in the main view because of a background image view. Did you find a workaround ?
-
Ömer Faruk Almalı about 10 yearsSo why didn't u share the fix?
-
rvijay007 about 10 yearsThis did not work for me. First got an error that required me to call - (void) layoutSubviews in DidLayoutSubviews, and after that, my tableView insets were still not correct. Even tried switching the views around in your method, but still did not work.
-
Gavin Hope over 9 yearsNote: if you're doing auto layout programatically, you might have something like
id topGuide = self.topLayoutGuide;
I found that having that line before adding theUITableView
as the first subview to myUIViewController
(in viewDidLoad) stopped the auto padding from working... -
Cemen over 9 yearsNot always. You can get auto padding in non-
UITableViewController
(at least I've got it). Subviews order makes sense there. If yourtableView
is subview #0 in your controller'sview
, it will auto-adjust. Else, it will stay as is. -
ideawu over 9 years@Cemen I have problem even if tableView is subview#0 when I set
navigationBar.translucent = NO;
. I works whennavigationBar.translucent = YES;
-
Cemen over 9 years@ideawu it will not place tableView under navigation bar when it's opaque. Instead, it should resize whole controller.view to start from navigation bar bottom, AFAIR.
-
Andy Obusek about 9 yearsThis seems so promising and encouraging, but it did not work for me either.
-
valbu17 about 9 yearsI had this issue for like 2 days and couldn't figured it out.. This deserves like 50+
-
koen about 9 yearsShouldn't it also call
[super viewDidLayoutSubviews];
? -
Christopher Pickslay about 9 years@Koen technically I suppose so. The default implementation is a no-op, so it would only matter if you have a custom intermediary superclass that also implements it.
-
Zeeshan over 8 years@Redwarp did you find the any way for your problem. I'm facing the same, I've a tableview as second subview (first is image view for background animation) in view controller. I want my tableview to start after navigation bar and cover the offset content on scroll to get the navigation bar blur effect. Please share, if you find any answers. Thanks.
-
Redwarp over 8 years@Zeeshan what I do lately is to deactivate this automatic adjustment of content inset, and do it by hand: I put the scrollview behind the navigation bar, and have the content inside the scrollview start at 64 points. And I also shift the scroll bars by 64 points. Ultimately, I believe that everything that does stuff for you "automatically" can become your enemy, because it's hard to control what happens.
-
Cristi Băluță over 7 yearsIt works but probably depends where you put the code. At first i put it in viewdidload and didn't work. Now it is called a little later after data is coming. My view under the tableview is a placeholder for empty tables. Not sure if there's a better practice but i need to have the pull to refresh functionality and the background to stay in place, that's why this setup.
-
Raj Pawan Gumdal over 7 yearsBeautiful and useful answer! It would have been great if Apple had forethought this and somehow provided this feature for not just UITableViewController or top most view of a view controller if it is scrollable but also to any possible scenarios where a scroll view can be deep inside the view hierarchy.