automaticallyAdjustsScrollViewInsets not working - ios

I've created an extremely simple demo app to test the functionality of automaticallyAdjustsScrollViewInsets, but the last cell of the tableView is covered by my tab bar.
My AppDelegate code:
UITabBarController *tabControl = [[UITabBarController alloc] init];
tabControl.tabBar.translucent = YES;
testViewController *test = [[testViewController alloc] init];
[tabControl setViewControllers:#[test]];
[self.window setRootViewController:tabControl];
My testViewController (subclass of UITableViewController) Code:
- (void)viewDidLoad
{
[super viewDidLoad];
self.automaticallyAdjustsScrollViewInsets = YES;
self.tableView = [[UITableView alloc] initWithFrame:self.view.bounds];
self.tableView.dataSource = self;
self.tableView.scrollIndicatorInsets = self.tableView.contentInset;
//[self.view addSubview:self.tableView];
// Do any additional setup after loading the view.
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 20;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#""];
cell.textLabel.text = #"test";
return cell;
}
Is this a bug in iOS 7? If not, what did I do wrong?

I think that automaticallyAdjustsScrollViewInsets only works when your controllers view is a UIScrollView (a table view is one).
You're problem seems to be that your controller's view is a regular UIView and your UITableView is just a subview, so you'll have to either:
Make the table view the "root" view.
Adjust insets manually:
UIEdgeInsets insets = UIEdgeInsetsMake(controller.topLayoutGuide.length,
0.0,
controller.bottomLayoutGuide.length,
0.0);
scrollView.contentInset = insets;
Edit:
Seems like the SDK is capable of adjusting some scroll views despite not being the controller's root view.
So far It works with UIScrollView's and UIWebView's scrollView when they are the subview at index 0.
Anyway this may change in future iOS releases, so you're safer adjusting insets yourself.

For automaticallyAdjustsScrollViewInsets to work, your view controller must be directly on a UINavigationController's stack, i.e. not as a child view controller within another view controller.
If it is a child view controller of another view controller which is on the navigation stack, you can instead set automaticallyAdjustsScrollViewInsets = NO on the parent. Alternatively you can do this:
self.parentViewController.automaticallyAdjustsScrollViewInsets = NO;

I just solved this issue with iOS 11 and swift 4, my current problem was that iOS11 has a new property to validate the insets when a ScrollView does exist, that one is contentInsetAdjustmentBehavior which is a ScrollView's property and the default property is automatic so my code was:
if #available(iOS 11, *) {
myScroll.contentInsetAdjustmentBehavior = .never
} else {
self.automaticallyAdjustsScrollViewInsets = false
}
I hope this solve your problems too...

I have this hierarchy:
custom navigationcontroller contains custom tabbarcontroller
custom tabbarcontroller contains several controllers
these controllers contains subviews and one of them contains a subclass of uiscrollview.
I had to set automaticallyAdjustsScrollViewInsets to NO
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
self.automaticallyAdjustsScrollViewInsets = NO;
in the custom tabbarcontroller. Other controllers in the hierarchy do not have any impact on the nested scroll view's behavior.

I was having the same issue, a Table View with unwanted top padding.
All answers say to fix by setting automaticallyAdjustsScrollViewInsets = NO, but that was not eliminating the padding for me.
Similar to the other answers here, these directions need to be tweaked slightly if you're using a non-standard view hierarchy.
I had a UIViewController with an embedded UITableViewController. It was not working to set automaticallyAdjustsScrollViewInsets on the Table View Controller.
Instead, I set automaticallyAdjustsScrollViewInsets = NO on the parent UIViewController that was embedding my Table View Controller. That successfully eliminated the padding on the Table View.

Related

UITableView with UISearchController go under navbar when enter in a result view and come back

I have a UITableView with a UISearchController search bar in the UINavigationBar, all works perfectly, but when I push a result of the searched results of the UISearchController, and I come back the UITableView is under the NavBar, this is how I initialize the UISearchController:
self.searchController = [[UISearchController alloc] initWithSearchResultsController:nil];
self.searchController.delegate = self;
self.searchController.searchResultsUpdater = self;
self.searchController.searchBar.delegate = self;
self.searchController.dimsBackgroundDuringPresentation = NO;
self.searchController.hidesNavigationBarDuringPresentation = NO;
self.searchController.searchBar.placeholder = NSLocalizedString(#"Local Search", #"");
self.searchController.searchBar.frame = CGRectMake(0, -5, [UIScreen mainScreen].bounds.size.width, 44);
ctrl = [[UIView alloc] initWithFrame:CGRectMake(0, 0,[UIScreen mainScreen].bounds.size.width, 44)];
ctrl.backgroundColor = [UIColor clearColor];
ctrl.autoresizingMask = UIViewAutoresizingFlexibleWidth;
[ctrl addSubview:self.searchController.searchBar];
self.navigationItem.titleView = ctrl;
self.definesPresentationContext = YES;
The search bar is displayed perfectly in the UINavigationBar, then when I search something and I push the view controller of one results like this:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[self.tableView deselectRowAtIndexPath:indexPath animated:YES];
DetailListController *detailList = [[DetailListController alloc] init];
[self.navigationController pushViewController:detailList animated:YES];
}
when I come back to the UITableView doing this:
[self.navigationController popViewControllerAnimated:YES];
the UITableView is under the UINavigationBar, how I can fix this?
thanks
I had exactly the same problem, setting extendedLayoutIncludesOpaqueBars to YES/true in view controller, that presents search controller, seems to fix it.
self.extendedLayoutIncludesOpaqueBars = YES
As a word of caution: setting this property changes value of scroll view's vertical content offset.
This is an extremely frustrating bug in UIKit. What appears to be happening is that the presenting view controller's top layout guide get reset to 0, meaning it is now underneath the nav bar. The layout guide is a read only property, so you can't fix it by editing directly. However, I did come up with a hack to get it to reset to the correct value. Add this to your UISearchControllerDelegate:
- (void)didDismissSearchController:(UISearchController *)searchController
{
UINavigationController *nav = self.navController; // you muse save this earlier
// force to layout guide to reset by pushing a dummy controller and popping it right back
[nav pushViewController:[UIViewController new] animated:NO];
[nav popViewControllerAnimated:NO];
}
I have used UISearchController for years but today is the first time I am facing this issue
I used to set edgesForExtendedLayout = [] and extendedLayoutIncludesOpaqueBars = true and everything is fine
but today It is not, I needed to reverse it to edgesForExtendedLayout = .all
it may be useful to you too!
Ran into this issue today and resolved using a reference to the result view controller's safeAreaLayoutGuide
After refreshing your result table's contents, you can then call this method:
// NOTE: this method is used to fix a known UIKit bug where search results that do not have a content
// size that fills the view will be placed underneath the navigation bar when displayed. this hack
// fixes this issue by resetting the contentInset based on the content size.
private func adjustContentInsetForContentSize() {
if collectionView.contentSize.height > view.frame.height {
collectionView.contentInset = UIEdgeInsets.zero
} else {
collectionView.contentInset = UIEdgeInsets(top: view.safeAreaLayoutGuide.layoutFrame.origin.y, left: 0, bottom: 0, right: 0)
}
}
Essentially the issue is caused by having a result view that has a contentSize height that is smaller than the viewable region for the view. The results render find when the contentSize.height > view.frame.height, so this hack will force the content insets to properly respect the safe area layout guide.

iOS Fix search bar on top of the UITableViewController?

I'm adding search bar on table header and floating it in scrollViewDidScroll method, but when i scroll without click on search bar(i.e. i go to the view and do scroll) then search bar doesn't stay on top but it scroll up with table however once i click on search bar and click cancel button on search bar and then if i scroll the table, search bar stays on top.here is my code-
-(void)viewDidLoad {
[super viewDidLoad];
UISearchBar *searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0, 0, 320, 44)];
searchBar.delegate = self;
searchDisplayController = [[UISearchDisplayController alloc] initWithSearchBar:searchBar contentsController:self];
searchDisplayController.delegate = self;
searchDisplayController.searchResultsDataSource = self;
searchDisplayController.searchResultsDelegate = self;
UIView *tableHeaderView = [[UIView alloc] initWithFrame:searchDisplayController.searchBar.frame];
[tableHeaderView addSubview:searchDisplayController.searchBar];
[tableView setTableHeaderView:tableHeaderView];
isSearching = NO;
}
-(void)scrollViewDidScroll:(UIScrollView *)scrollView {
UISearchBar *searchBar = searchDisplayController.searchBar;
CGRect searchBarFrame = searchBar.frame;
if (isSearching) {
searchBarFrame.origin.y = 0;
} else {
searchBarFrame.origin.y = MAX(0, scrollView.contentOffset.y + scrollView.contentInset.top);
}
searchDisplayController.searchBar.frame = searchBarFrame;
}
- (void)searchDisplayControllerWillBeginSearch:(UISearchDisplayController *)controller {
isSearching = YES;
}
-(void)searchDisplayControllerWillEndSearch:(UISearchDisplayController *)controller {
isSearching = NO;
}
Note that I'm using UITableViewController sub class and don't want to change it to UIViewController.
Any help would be appreciated.
Edit: I also using section header in this UITableViewController, in other UITableViewController there is no section header and this code working fine.Is this a problem with section header and table header together?
The reason why your searchbar is scrolling with the table contents is that you have put it directly IN the table, thus making it a child header section of the table. and that section ALWAYS scrolls…
Here is how this this can be achieved. And it is actually quite simple. (The following example relies on Storyboard, but the mechanism is the same whatever you are using) :
1) Use a UIVIewController and NOT a UITableViewController
2) Add a UITableView as the child of the parent UIView
3) Add a UISearchBarController also as a child view of the UIView, NOT as a child of the UITableView (UITableView and UISearchController are siblings)
you should have the following layout :
EDIT : The important thing to remember is to put the UISearchBarController ABOVE the sibling UITableView. Otherwise you may see the UITableView overlap the UISearchBarController when the latter is focused.
EDIT 2 : BTW, if you are using AutoLayout, remember to set the TOP constraint of the tableView relative to the SearchBar…
Run it and admire the result.
Hope this helps.
There is not a way to maintain the header of a tableView fixed
1- could use an UIViewController instead of UITableViewController.
2- add subview (UIView) for header.
3- and add another subview for the tableview.

UISearchBar moves down 20 pixels

I have a UITableViewController T that contains a UISearchDisplayController and UISearchBar in the standard way (everything defined in a nib). T is then contained as a child view controller in the standard way by some other view controller.
The search bar displays correctly:
But when I tap into the search bar do actually search, the search bar gets taller, and the content inside the search bar drops down by what looks like about 20 pixels (er, the height of the status bar? coincidence?) Like so:
What's up with that? It's too tall. Also, the animation that makes it look like that is ungainly. Any way to prevent that unsightly growth?
I managed to solve a very similar problem to the one you're experiencing by programmatically setting the UISearchController up like so:
Note ~ The error persists if hidesNavigationBarDuringPresentation is not set to NO.
#pragma mark - View Life Cycle
- (void)viewDidLoad {
[super viewDidLoad];
self.results = [[NSArray alloc] init];
// 1 \\ Results Table View
UITableView *searchResultsTableView = [[UITableView alloc] initWithFrame:self.tableView.frame];
[searchResultsTableView registerClass:[UITableViewCell class] forCellReuseIdentifier:Identifier];
searchResultsTableView.delegate = self;
searchResultsTableView.dataSource = self;
// 2 \\ init search results table view & setting its table view
self.searchResultsTableViewController = [[UITableViewController alloc] init];
self.searchResultsTableViewController.tableView = searchResultsTableView;
self.searchResultsTableViewController.view.backgroundColor = [UIColor blackColor];
// 3 \\ init a search controller with it's tableview controller for results
self.searchController = [[UISearchController alloc] initWithSearchResultsController:self.searchResultsTableViewController];
self.searchController.hidesNavigationBarDuringPresentation = NO; // √
self.searchController.searchResultsUpdater = self;
self.searchController.delegate = self;
self.searchController.searchBar.barTintColor = [UIColor blackColor];
// 4 \\ Make an appropriate search bar (size, color, attributes) and add it as the header
[self.searchController.searchBar sizeToFit];
self.tableView.tableHeaderView = self.searchController.searchBar;
self.tableView.tableHeaderView.backgroundColor = [UIColor blackColor];
// 5 \\ Enable presentation context
self.definesPresentationContext = YES;
self.tableView.backgroundColor = [UIColor clearColor];
self.currentUser = [PFUser currentUser];
}
Added Further Details:
Interface Builder: UIViewController (not UITableViewController)
Add a UITableView with Constraints:
a.
b. *My Nav Bar is custom height - your "Top Space to: Top Layout Guide" may be changed
Add the Delegate & Data Source outlets from the added Table View to the UIViewController
a. Don't forget to do so programmatically:
#interface AddUsersViewController : UIViewController <UITableViewDelegate, UITableViewDataSource>
#property (weak, nonatomic) IBOutlet UITableView *tableView;
In your Interface Extension:
a. Set your UISearchConroller's Delegate and Data Source:
#import "AddUsersViewController.h"
#define ResultsTableView self.searchResultsTableViewController.tableView
#define Identifier #"Cell"
#interface AddUsersViewController () <UISearchControllerDelegate, UISearchResultsUpdating>
#property (nonatomic) UISearchController *searchController;
#property (nonatomic) UITableViewController *searchResultsTableViewController;
#property (nonatomic, weak) UIBarButtonItem *leftBarButton;
#property (nonatomic) NSArray *results;
#end
Although this worked for me, I have discovered that there are other possible scenarios that could be causing this. My "issue" wasn't that the search bar moved down 20px (or so) but that it moved up. I attributed this to a customized UINavigationBar header that included an image title and custom NavBarButtons. With that in mind, here are two more possible solutions:
.
Original Scenarios:
Your Container View's extendedLayoutIncludesOpaqueBars Property
a. Storyboard: Check √ "Under Opaque Bars" for the table views in storyboard.
b. Programmatically: yourTableView(s).extendedLayoutIncludesOpaqueBars = YES;
.
Set scopeButtonTitles to an empty array. (below)
self.searchController = UISearchController(searchResultsController: nil);
self.searchController.searchResultsUpdater = self;
self.searchController.searchBar.scopeButtonTitles = [];
self.tableView.tableHeaderView = self.searchController.searchBar;
The later two answers come from THIS question and may prove to be beneficial in checking out too.
Thanks to BananaNeil's research and testing, he discovered that if you follow the my recommendations and set your searchController.searchBar.y = 0 your search bar should be exactly where you were hoping it would be. √
Not sure if this is iOS 9 specific, but setting
searchController.hidesNavigationBarDuringPresentation = NO;
solves the issue of the weird downward movement of the UISearchBar all over the containing UITableView. Of course, this does disable the nav bar - so this might not be what you want.
I have actually figured out my issue:
I had my UISearchBar inside of a wrapper view, which I was re-positioning as the UITableView scrolled.
The wrapper was 20px taller than the searchbar, and I set searchbar.y = 20 (for stylistic reasons). Each time I clicked on the search bar, it inexplicably grew by 20px.
At some point, I set the searchBar.y = 10, and noticed that it only grew by 10px.
And sure enough, it turns out that the growth height of the searchBar is exactly equal to it's y-position within it's immediate superview. So, when I set the searchbar.y = 0, the searchbar stopped growing.
Instead of using a UITableViewController, select a normal UIViewController and add a UITableView to its view. Add constraints to pin it to Top Layout Guide.Bottom, Superview.Leading, Superview.Trailing, Bottom Layout Guide.Top. Then add the Search Bar to the UITableView.
For some reason the Search Bar doesn't jump when implemented this way.
One possible reason could be that while editing the Search Controller adjusts the position of the Search Bar by offsetting the height of the Top Layout Guide. Pinning the Table View top explicitly to Top Layout Guide.Bottom helps somehow.
Hope this helps!
Please try using:
yourSearchBar.clipsToBounds = true
Swift 5.x solution:
Setting edgesForExtendedLayout to none or empty will fix this issue.
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.edgesForExtendedLayout = []
}
In the lower versions of swift the usage will be similar to below:
self.edgesForExtendedLayout = .None
self.edgesForExtendedLayout = UIRectEdgeNone

Why does UIViewController extend under UINavigationBar, while UITableViewController doesn't?

I have UITabbarController with UINavigationController in it. I have a subclass of UIView that I assign as the view of UIViewController in the navController. This is pretty standard stuff, right? This is how I do it
_productCategoryView = [[ProductCategoryView alloc] initWithFrame:self.view.frame];
self.view = _productCategoryView;
This view has a UITableView as subView
_productCategoryTableView = [[UITableView alloc] initWithFrame:self.frame style:UITableViewStylePlain];
_productCategoryTableView.separatorStyle = UITableViewCellSeparatorStyleNone;
_productCategoryTableView.backgroundColor = [UIColor clearColor];
[self addSubview:_productCategoryTableView];
For the sake of debugging I am setting self.backgroundColor = [UIColor blueColor] on the view.
From the above initialization of tableView one might think that the view's and table's frame is same. However when I run in iOS 7, the view's origin is set behind the UINavigationBar. This is understandable because I am setting self.navigationBar.translucent = YES; in my subclass of UINavigationController. But what I don't understand is how come the table is sitting just below the navBar? Shouldn't it also start from (0, 0) which is behind the navBar? See screenshot Scenario 1 below. Notice the blue hue behind navBar
Now, I push another viewController on the navigation stack, simply by using [self.navigationController pushViewController.....]. Again I have a custom UIView with a tableView in it. However I also have a UILabel above this table, and again for debugging, I gave it a redColor. This time I am setting the label's origin to be almost same as the view's
CGRect boundsInset = UIEdgeInsetsInsetRect(self.bounds, UIEdgeInsetsMake(10, 10, 10, 10));
CGSize textSize = [_titleLabel.text sizeWithFont:_titleLabel.font
constrainedToSize:CGSizeMake(boundsInset.size.width, MAXFLOAT)
lineBreakMode:NSLineBreakByWordWrapping];
printSize(textSize);
_titleLabel.frame = CGRectMake(boundsInset.origin.x,
boundsInset.origin.y,
boundsInset.size.width,
textSize.height);
So, going by the logic above, the label should be visible, right? But this time it's not. This time the label is behind the navBar.
Notice, the red hue behind navBar.
I would really like to align the subView below the navBar consistently. My questions are
1. How is the tableView offset by 64pixels (height of nav + status bar in iOS 7) automatically, even though it's frame is same as the view's?
2. Why does that not happen in the second view?
By default, UITableViewController's views are automatically inset in iOS7 so that they don't start below the navigation bar/status bar. This is controller by the "Adjust scroll view insets" setting on the Attributes Inspector tab of the UITableViewController in Interface Builder, or by the setAutomaticallyAdjustsScrollViewInsets: method of UIViewController.
For a UIViewController's contents, if you don't want its view's contents to extend under the top/bottom bars, you can use the Extend Edges Under Top Bars/Under Bottom Bars settings in Interface Builder. This is accessible via the edgesForExtendedLayout property.
Objective-C:
- (void)viewDidLoad {
[super viewDidLoad];
self.edgesForExtendedLayout = UIRectEdgeNone;
}
Swift 2:
self.edgesForExtendedLayout = UIRectEdge.None
Swift 3+:
self.edgesForExtendedLayout = []
#Gank's answer is correct, but the best place to do this is on the UINavigationControllerDelegate (if you have one):
func navigationController(navigationController: UINavigationController, willShowViewController viewController: UIViewController, animated: Bool) {
viewController.edgesForExtendedLayout = UIRectEdge.None
}

UIRefreshControl hidden / obscured by my UINavigationController's UINavigationBar

I'm attempting to use a UIRefreshControl inside my UITableViewController which itself is inside a UINavigationController, which has its hidesNavigationBar property set to NO (so the navigation bar is visible).
The UIRefreshControl works, but is obscured by the UINavigationBar. I'm surprised I can't find anyone else who has run into this problem.
Possible relevant points:
I set the rootViewController of my UIWindow to be my UINavigationController.
I set the initial view controller of the UINavigationController by setting the viewControllers property of the UINavigationController.
My UITableViewController subclass is instantiated with a nib.
I instantiate my UIRefreshControl in the viewDidLoad method of my UITableViewController subclass. I set the refreshControl property of the UITableViewController subclass in this method.
The UIRefreshControl works perfectly fine, and I can see a portion of it, but it is obscured by my UINavigationBar. It looks completely normal if I set hidesNavigationBar to YES (but I don't want to hide it).
Edit:
The code used to create and position my UIRefreshControl is:
UIRefreshControl *refreshControl = [[UIRefreshControl alloc] init];
[refreshControl addTarget:self
action:#selector(toggleRefresh:)
forControlEvents:UIControlEventValueChanged];
self.refreshControl = refreshControl;
This code snippet is in the viewDidLoad method of my UITableViewController subclass, which is a child view controller of a UINavigationViewController.
For those targeting iOS 7, there seems to be a new issue present where the UIRefreshControl is drawn behind the UITableView's backgroundView. I experienced this both when initializing the UIRefreshControl programatically and from a storyboard. A simple workaround is to update the zPosition of the UIRefreshControl in viewDidLoad of your UITableViewController:
self.refreshControl.layer.zPosition = self.tableView.backgroundView.layer.zPosition + 1;
I've find a real solution, here it is:
I've a UIViewController inside a UINavigationController with a translucent NavigationBar. Inside the UIViewController there is the UITableView.
I want to add a UIRefreshControl but when I do it, it's hidden by the NavigationBar, like you explain.
Here is my code to make it work:
// Add a UITableViewController
self.tblViewController = [[UITableViewController alloc] init];
// Set the UITableView to it
self.tblViewController.tableView = self.tblView;
// Initialize the UIRefreshControl and set it's method
self.refreshControl = [[UIRefreshControl alloc] init];
[self.refreshControl addTarget:self action:#selector(refreshTable) forControlEvents:UIControlEventValueChanged];
// Set the RefreshControl to the UITableViewController
self.tblViewController.refreshControl = self.refreshControl;
// Here is the thing ! Just change the contentInset to push down the UITableView content to 64 pixels (StatusBar + NavigationBar)
self.tblView.contentInset = UIEdgeInsetsMake(64.f, 0.f, 0.f, 0.f);
With this code, your UITableViewController will show the RefreshControl perfectly and keep the translucent NavigationBar effect when you scroll down the cells.
It looks like a bug to me, because it only occures when the contentOffset property of the tableView is 0
see this question
UIRefreshControl not showing spiny when calling beginRefreshing and contentOffset is 0
I fixed that with the following code (method for the UITableViewController) :
- (void)beginRefreshingTableView {
[self.refreshControl beginRefreshing];
if (self.tableView.contentOffset.y == 0) {
[UIView animateWithDuration:0.25 delay:0 options:UIViewAnimationOptionBeginFromCurrentState animations:^(void){
self.tableView.contentOffset = CGPointMake(0, -self.refreshControl.frame.size.height);
} completion:^(BOOL finished){
}];
}
}
In iOS 7, self.view is under the navigationBar, except that you write something as follows,
self.edgesForExtendedLayout = UIRectEdgeNone; // or UIRectEdgeAll & ~UIRectEdgeTop
or
self.navigationViewController.navigationbar.translucent = NO;
Using #Jonathan's answer I got this working well. But since I am using storyboard I set the content inset there like so: Which in case someones needs:
(xcode 6.4)
Do not call -setTranslucent: on your UINavigationBar. Then, your refresh control will be positioned properly, below the navigation bar.

Resources