iOS Fix search bar on top of the UITableViewController? - ios

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.

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.

Table View Hide Searchbar until scrolled

I got a table view set up, and a working search bar - NO XIB OR STORYBOARD involved. Above the search bar I have label showing the numbers of entries in the table view and some other stuff. Well now I want the search bar + label hidden until the user scrolls up (like in Music App). This is the setup of my search bar
self.searchBar = [[UISearchBar alloc]initWithFrame:CGRectMake(0, 0, 320, 44)];
self.searchBar.showsCancelButton = YES;
[self.view addSubview:self.searchBar];
self.searchBar.delegate = self;
self.tableView.contentInset = UIEdgeInsetsMake(self.searchBar.frame.size.height,0, 0, 0);
[self.tableView setTableHeaderView:self.searchBar];
[self.tableView setContentOffset:CGPointMake(0,88) animated:YES];
[self.zsearchDisplayController setActive:NO animated:YES];
This is my label:
tableCountDisplay = [[UILabel alloc]initWithFrame:CGRectMake(5, -44, 155, 44)];
The label is already hidden until the user scrolls. The problem is - I can't get the search bar to hide. If I do
[self.tableView setContentOffset:CGPointMake(0,88) animated:YES];
Then search bar and label are hidden but also the first element of my table view...
If I do 44 or 0 (doesn't matter which of them)
[self.tableView setContentOffset:CGPointMake(0,44 or 0) animated:YES];
the label is hidden, and everything else is visible. Technically 0,44 should be the right offset, but it does not work for some reason.
I'd be really happy about some help!
One of the solutions is to add your searchBar and your label as subViews to a UIView. Then set this UIView as the TableHeaderView. My working sample looks like this:
Screen before scrolling:
Screen after scrolling:
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 (add the searchBarView in this view).
3- and add another subview for the tableview.
What about
tableView.contentOffset = CGPoint(x: 0, y: (tableView.tableHeaderView?.frame.size.height ?? 0))
on wiewWillAppear: ;)

Programatically implement a searchbar that doesn't scroll

I'm working on implementing a basic searchbar progmatically. I can't figure out how to make the searchbar stick to the header as the table scrolls. here is my code for loading the searchbar :
-(void)loadbar{
searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0, 0, 320, 44)];
searchDisplayController = [[UISearchDisplayController alloc] initWithSearchBar:searchBar contentsController:self];
searchDisplayController.delegate = self;
searchDisplayController.searchResultsDataSource = self;
self.tableView.tableHeaderView = searchBar;
}
Search bar inherits from UISearchBar and searchDisplayController inherits from UISearchDisplayController
Thanks!
A table's header view scrolls with the table. If you don't want the search bar to scroll with the table view, you can't set the search bar as the header view.
You have three options.
Add the search bar to the navigation bar
Make the search bar a subview of the tableview. Implement the scrollViewDidScroll: delegate method for the table view and adjust the position of the scroll bar as the table scrolls.
Don't use UITableViewController. Use UIViewController and add your own table view. Add the search bar at the top and the table view below the search bar.
Solution
Use a one-section table view and set the search bar as the section header.
Discussion
Before, iOS was able to recognize when a search bar was in the table view header. In that case, the search bar was hidden under the navigation bar and it was possible to scroll down to reveal it. When in use, the search bar was fixed at the top of the window, not scrolling with the table view content. Since recently, this behavior is broken: the search bar scrolls with the cells.
In the proposed solution, we set the search bar as a section header. A section header remains visible until we scroll passed the end of that section. Thus, if we only have one section, the search bar is always visible.
Objective-C
// Class members
UISearchBar *searchBar;
- (void)viewDidLoad {
// Inherited
[super viewDidLoad];
// Search Bar
searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0, 0, 320, 44)];
searchBar.delegate = self;
searchBar.barStyle = UISearchBarStyleMinimal;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// It is mandatory to have one section, otherwise the search bar will scroll
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// TODO: Return the number of elements in the table
return 1;
}
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
return searchBar.frame.size.height;
}
-(UIView*)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
return searchBar;
}

Adding a view on top of a UITableViewController that is initially hidden until the user pulls down

I want to add a search on top of a UITableViewController, but I want the field to be hidden initially (like Notes app) until the user pulls down to reveal it. How can I achieve this?
Thank you,
You should use UITableView's tableHeaderView. When done in code, it looks like this:
-(void)viewDidLoad
{
[super viewDidLoad];
UISearchBar *searchBar = [[UISearchBar alloc]initWithFrame:CGRectMake(self.tableView.bounds.origin.x, self.tableView.bounds.origin.y, self.tableView.bounds.size.width, 44.0)];
self.mySearchDisplayController = [[UISearchDisplayController alloc] initWithSearchBar:searchBar contentsController:self];
self.mySearchDisplayController.searchResultsDelegate = self;
self.mySearchDisplayController.searchResultsDataSource = self;
self.mySearchDisplayController.delegate = self;
self.tableView.tableHeaderView = searchBar;
}
You can use a tableview searchbar with the searchbar delegate.
On the viewDidLoad method you can manually scroll up the tableview. You can set an IBOulet on the searchbar to access its frame's height to know its height.

automaticallyAdjustsScrollViewInsets not working

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.

Resources