I have a UITableViewController subclass, displayed in a modal view on an iPad. The view controller has a UISearchDisplayController subclass with a UISearchBar included in the header view of the table.
The subclassed UISearchDisplayController is called NoAnimationSearchDisplayController and I have overridden the - (void)setActive:(BOOL)visible animated:(BOOL)animated
method to prevent the search bar from animating into the navigation bar when it's set to active. The method override is below...
- (void)setActive:(BOOL)visible animated:(BOOL)animated
{
if (self.active == visible) {
return;
}
[self.searchContentsController.navigationController setNavigationBarHidden:YES animated:NO];
[super setActive:visible animated:animated];
[self.searchContentsController.navigationController setNavigationBarHidden:NO animated:NO];
if (visible) {
[self.searchBar becomeFirstResponder];
} else {
[self.searchBar resignFirstResponder];
}
}
The problem I'm having, is that when I have searched, and results are displayed in my search display controller's table view, everything looks fine untill i try to scroll down the list, at this point the content within the cells appears above the search bar, as shown in the following screen:
The search bar is set to UISearchBarStyleMinimal and transparency is enabled. Can anyone let me know how to stop this content overlapping the search bar? Ideally the content would disappear under the search bar as if it was the end of the view.
The answer was to manually change the frame of the table view provided by the UIsearchDisplayController in the appropriate delegate method...
- (void)searchDisplayController:(UISearchDisplayController *)controller willShowSearchResultsTableView:(UITableView *)tableView {
/**
* Remove content inset automatically set by UISearchDisplayController as we are forcing the
* search bar to stay in the header view of the table, and not go into the navigation bar.
*/
[tableView setContentInset:UIEdgeInsetsMake(0.0f, 0.0f, 0.0f, 0.0f)];
/**
* Recalculate the bounds of our table view to account for the additional 44 pixels taken up by
* the search bar, but store this in an iVar to make sure we only adjust the frame once. If we
* don't store it in an iVar it adjusts the frame for every search.
*/
if (CGRectIsEmpty(_searchTableViewRect)) {
CGRect tableViewFrame = tableView.frame;
tableViewFrame.origin.y = tableViewFrame.origin.y + 44;
tableViewFrame.size.height = tableViewFrame.size.height - 44;
_searchTableViewRect = tableViewFrame;
}
[tableView setFrame:_searchTableViewRect];
}
Be sure the search bar is not translucent enabled ( on storyboard/xib ) or by code.
Then make the background to white or whatever color you want.
Related
I have a hidden searchBar in a tableView header:
- (void)viewWillAppear:(BOOL)animated {
CGRect newBounds = self.tableView.bounds;
newBounds.origin.y = newBounds.origin.y + self.searchBar.bounds.size.height;
self.tableView.bounds = newBounds;
}
I also have a magnifying-glass icon button that reveals the search bar:
- (IBAction)showSearchBar:(id)sender {
[self.searchDisplayController setActive:YES animated:YES];
[self performSelector:#selector(showKeyboard) withObject:nil afterDelay:0.1];
}
Problem is, when I pull down the table (to reload data) - it also shows the search bar.
I only want the searchBar to become visible when the magnifying-glass icon gets tapped.
I am not really sure, how to address this problem?
Am I hiding it the wrong way in the first place?
Thanks,
Added a screenshot to make my question more clear:
searchbar appears when pulling down
I don't tried that out, but I can image that you just have to hide it. for example:
[[self.searchDisplayController view] setHidden:YES]
I have a UITableViewController with many cells, each cell contains four text fields vertical on each other, a pop over is presented by tapping on any text field, however, this pop over contains a text field when tapped the keyboard is fired and most likely the pop over will be shifted up to prevent the keyboard from hiding its text field (this is the default behavior of the pop over), but in the background (the dimmed view), the tableViewController loses its correct scrolling behavior to keep the pop over presenting-textField on the track while the keyboard is visible ..
a sample project can be downloaded here.
how can I offset the table view to keep the pop over presenting-textField on screen while keyboard is visible in this case ?
I tried the well-known TPKeyboardAvoiding library but it didn't solve the issue.
p.s. tableViewController works well for the first 3 or 4 keyboard firings, but loses precise scrolling on later attempts.
Screenshot (the green text field is the text field which presented the pop over, but tableViewController scrolls to the incorrect text field indicated in red):
any help would be highly appreciated.
EDIT:
this question is not a duplicate for: Making a UITableView scroll when text field is selected
because the text field that I need the table view to scroll to is the one that fires a pop over not a keyboard, and scrollToRowAtIndexPath does not work precisely in this case because each cell contains 4 text fields.
use (CGRect)convertRect:(CGRect)rect toView:(UIView *)view to get cell position on tableviews superview and accordingly handle the tableview offset
Here is my solution :
Change into
TableViewController.m
1. Register for keyboard Notifications (UIKeyboardWillShowNotification, UIKeyboardWillHideNotification)
2. Create local variables:
CGSize _currentPopoverContentSize; //if you want to have custom size for popover
UIView *_currentPopoverSender; //to remember from wich view you will present popover
BOOL _keyboardIsShown; //enable in keyboardWillShow, and disable in keyboardWillHide
3. In my presentPopover method:
- (void)presentPopoverControllerWithSize:(CGSize)size fromView:(UIView *)sender{
MyController *controller = [[[MyController alloc] init] autorelease];
if (self.popover)
{
[_popover release];
_popover = nil;
}
_popover = [[UIPopoverController alloc] initWithContentViewController:controller];
_popover.popoverContentSize = size;
_popover.delegate = self;
//checking if keyboard is shown - if NO, than present popover, if YES - just `resignFirstResponder` for your _`activeTextField`(you can set it in -textFieldDidBeginEditing: and nullify in -textFieldDidEndEditing:)
if (!_keyboardIsShown)
{
[_popover presentPopoverFromRect:[sender bounds]
inView:sender
permittedArrowDirections:UIPopoverArrowDirectionUp
animated:YES];
_popOver.popoverContentSize = CGSizeMake(320, 700);
}
else
{
[_activeTextField resignFirstResponder];
}
_currentPopoverContentSize = size;
_currentPopoverSender = sender;
}
4. Than:
- (void)keyboardWillBeHidden:(NSNotification*)aNotification{
[UIView animateWithDuration:0.3
animations:^{
//do some stuff
_popOver.popoverContentSize = CGSizeMake(320, 700);
} completion:^(BOOL finished) {
if (_popover && _currentPopoverSender)
{
[_popover presentPopoverFromRect:[_currentPopoverSender bounds]
inView:_currentPopoverSender
permittedArrowDirections:UIPopoverArrowDirectionUp
animated:YES];
}
}];
_keyboardIsShown = NO;
}
5.:
-(void)textFieldDidBeginEditing:(UITextField *)textField{
[textField resignFirstResponder];
[self presentPopoverControllerWithSize:textField.frame.size fromView:_activeText];
// self.vc = [[self storyboard] instantiateViewControllerWithIdentifier:#"ProductSourcePopOver"];
//
// if(_popOver == nil){ //make sure popover isn't displayed more than once in the view
// _popOver = [[UIPopoverController alloc]initWithContentViewController:self.vc];
// }
// _popOver.popoverContentSize = CGSizeMake(320, 700);
//
// [_popOver presentPopoverFromRect:_activeText.frame inView:_activeText permittedArrowDirections:UIPopoverArrowDirectionLeft animated:YES];
// _popOver.delegate = self;
}
This might helps you :)
Change the entire view frame size.
For example, keyboard height is 100 then,
CGRect frame = self.view.frame;
self.view.frame = CGRectMake(frame.origin.x, frame.origin.y, frame.size.width, frame.size.height - keyboardHeight);
Then call didSelectRowAtIndexPath method again for the same indexPath, so it will reload the popover.
This is just a base idea, there might be some modifications required to achieve exact behavior.
Hope this will help you.
How about add the tag(00 01 02 03, 10 11 12 13...etc) for each UITextfield in the cell, From tens digit you can know which cell, from single digits you know which textfield.
Register for UIKeyboardWillShowNotification and when that selector gets triggered, call your pop over code:
[self presentPopoverControllerWithSize:textField.frame.size fromView:_activeText];
The reason for this is that the iOS handles itself presenting the popover and handling it's frame. So it's generally a good practice to present the pop over after the keyboard is shown.
Hope this helps..
I have a UITableViewController with a UISearchBar and UISearchDisplayController. That exists inside a Container View in a UIViewController which is in a UINavigationController. I made this image to help describe the structure:
This is what it really looks like:
When I tap the Search Bar, I have to hide the Nav Bar. Normally, this would happen on its own, but since my UITableViewController is inside a Container View, I have to handle that change myself. This is what it looks like then, note that the Status Bar is white because the Nav Bar is white, even though it is Hidden at the moment.
Once I start typing in some search text, the results show up. If I scroll those results upward, they pass underneath the Search Bar, but they overlap the Status bar which is very unattractive.
If the Container View isn't involved, then this all works as intended and the table content passes underneath the Status Bar, but with the ContainerView involved, the table text and status bar collide.
How do I get the text to travel under the Status Bar like normal?
I have search this for hours and my final result was to put this line in viewDidLoad:
self.extendedLayoutIncludesOpaqueBars = YES;
Problem solved :)
Try setting the definesPresentationContext in viewDidLoad of your TableViewController
Swift
override func viewDidLoad() {
super.viewDidLoad()
definesPresentationContext = true
}
Objective-C
- (void)viewDidLoad {
[super viewDidLoad];
self.definesPresentationContext = YES;
}
Here's what worked for me:
DO:
Use UISearchController (not a separately placed UISearchBar)
Place your VC in a UINavigationController if it isn't already. Set the nav not to "Show Navigation Bar" if desired.
Use autolayout for the UITableView (not springs and struts) and pin the top of the table to the top of the VC's view.
Add this delegate method:
- (UIBarPosition)positionForBar:(id<UIBarPositioning>)bar {
return UIBarPositionTopAttached;
}
DON'T:
Fiddle with edgesForExtendedLayout
Fiddle with extendedLayoutIncludesOpaqueBars
Fiddle with the table's contentInset
Basically this is due to the traslucency of the nav bar, usually the view controller fix that overlapping, by correcting the top insets of the owned view or subview if they are(or inherits) from UIScrollView. You have 2 options, one is to set the traslucency of the navbar to no, the other is set the edgeForExtendedLayout to none ore leave only bottom.
- (void)searchDisplayControllerWillBeginSearch:(UISearchDisplayController *)controller {
self.navigationController.navigationBar.translucent = YES;
}
- (void)searchDisplayControllerDidEndSearch:(UISearchDisplayController *)controller {
self.navigationController.navigationBar.translucent = NO;
}
These advices works only on iOS7, if you are deploying on lower target check before settings those properties.
Another way around, but I didn't tested could be read the --topLayoutGuide length and in the -searchDisplayControllerWillBeginSearch try to set a topInsets of the same length. In this way you should still preserve the translucency.
I have UISearchBar and UISearchDisplayController.
In viewdidload:
self.edgesForExtendedLayout = UIRectEdgeNone;
[searchDisplayController.searchBar setBackgroundImage:[self imageWithColor:ETSBaseColor] forBarPosition:0 barMetrics:UIBarMetricsDefault];
method that obtain image from UIColor:
- (UIImage *)imageWithColor:(UIColor *)color
{
CGRect rect = CGRectMake(0.0f, 0.0f, 1.0f, 1.0f);
UIGraphicsBeginImageContext(rect.size);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, [color CGColor]);
CGContextFillRect(context, rect);
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
I had the same problem:
- (void)searchDisplayControllerWillBeginSearch:(UISearchDisplayController *)controller
{
controller.searchBar.searchBarStyle = UISearchBarStyleDefault; // Used to cover UIStatusBar
}
- (void)searchDisplayControllerWillEndSearch:(UISearchDisplayController *)controller
{
controller.searchBar.searchBarStyle = UISearchBarStyleMinimal; // Used not to show top and bottom separator lines
}
In my case I don't want to hide the UINavigationBar but I had similar problems with gapes and other side effects. One of them was a missing UISearchBar after switching between UIViewControllers while the UISearchDisplayController is visible (I'm using SWRevealViewController to switch between UIViewController). This problem occurs only on iPads. It came out that the UISearchBar suddenly hides behind the UINavigationBar. Now I solved all my Problems with the following lines of code in the UITableViewController which is presented in a UIContainerView:
- (UINavigationController *)navigationController {
return nil;
}
Those lines prevent the UISearchDisplayController to reach and change my UINavigationController. I also subclassed this method into "MyContainerTableViewController" class and use this class now for all embedded UITableViewController.
I'm still using UISearchDisplayController to Support iOS 7.
The following hack worked for me:
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
return (self.searchController.isActive && section == 0) ? 22.0f : 0.0f;
}
I am building an iOS 7-only app. I am trying to set a UISearchDisplayController into the navigation bar.
I have it set up like this: In the storyboard, I added a "Search Bar and Search Display Controller" to my view controller's view, and set it at (0,0) relative to the top layout guide. I set constraints to pin to left, top and right. (I played with the constraints, i removed them completely, it doesn't matter) On top of that I have my Table view. When I added the search bar to the view in the storyboard, it automatically setup outlets for searchDisplayController and searchBar delegate. In code I have self.searchDisplayController.displaysSearchBarInNavigationBar = YES; I have two problems:
1) Without any buttons showing for the search bar (Interface builder -> select search bar -> Options: none selected) the search bar is in the middle of the screen:
If I click on the navigation bar, it starts editing the search bar:
notice also that the dark overlay appears to be offset from the navigation bar. It seems to me that the space is the same height as the navigation bar. Like it has been shifted down by that much. Also, when it displays the search results, the top of the content view is shifted down by the same amount (more pictures follow), which brings me to the second problem.
2) I messed around with it for a while and decided to check the option to have it show the cancel button. Now I have the search bar embedded in the nav bar correctly, but the overlay is still shifted down:
Again, when the search results table view appears, it is shifted down by the same amount (notice the scroll bar on the right side):
Even more bizarrely, I set a border on the search display controller's tableview layer, and it appears correct:
I have never used the UISearchDisplayController before and I unfamiliar with how to set it up, but functionally it works fine. I have read some other similar posts but the only advice is to hack it up by adjusting frames and setting manual offsets. I'd prefer to know what is causing this, is it a bug? Something I'm doing wrong? If it's a bug I can wait for a fix. It seems like such a basic thing that a thousand people must have done without any problem so I feel like I'm not setting it up correctly somehow. Thanks for you input.
I remember running into the same exact problem that you are observing.There could be a couple of solutions you can try.
If you are using storyboards
You should click on the view controller or TableView Controller which you have set up for your tableview and go to its attribute inspector and look under ViewController section and set the Extend Edges section to be under Top Bars.
If you are not using storyboards you can manually set the settings using the viewcontrollers edgesForExtendedLayout property and that should do the trick. I was using storyboards.
In my case, using storyboards, I had to check both Under Top Bars and Under Opaque Bars and leave Under Bottom Bars unchecked.
In my case, I actually had to uncheck all the Extended Edges boxes (essentially the same as programmatically setting Extended Edges to UIRectEdgeNone I believe) in my Storyboard in order to stop my search bar from offsetting itself. Thank you guys!
definesPresentationContext = true
override func viewDidLoad() {
super.viewDidLoad()
searchController = UISearchController(searchResultsController: nil)
searchController.searchResultsUpdater = self
searchController.hidesNavigationBarDuringPresentation = false
searchController.dimsBackgroundDuringPresentation = true
searchController.searchBar.searchBarStyle = UISearchBarStyle.Prominent
self.tableView.tableHeaderView = searchController.searchBar
definesPresentationContext = true
or see UISearchBar presented by UISearchController in table header view animates too far when active
My problem was just Adjust scroll view inserts. After change to false I didn't have problem
I had a same problem. And I solve this issue with adding view object under the tableview.
Add new ViewController on the Storyboard
Drag TableView to the new VC
Drag Table Cell to the TableView
Make a Connection for TableView DataSource, TableView Delegate to the new VC
I had very similar behavior happening. For me, the solution was to uncheck Extend Edges Under Top Bar in the storyboard settings for the parent view controller (I've turned off transparent navbars, not sure if that effects anything). If you're not using storyboard, you have to set [UIViewController edgesForExtendedLayout].
From the Apple docs:
This property is only applied to view controllers that are embedded in containers, such as UINavigationController or UITabBarController. View controllers set as the root view controller do not react to this property. Default value is UIRectEdgeAll.
Unfortunately none of the above solutions worked for me, I'm using a UITableViewController.
This link helped:
http://petersteinberger.com/blog/2013/fixing-uisearchdisplaycontroller-on-ios-7/
I put the code below for convenience:
static UIView *PSPDFViewWithSuffix(UIView *view, NSString *classNameSuffix) {
if (!view || classNameSuffix.length == 0) return nil;
UIView *theView = nil;
for (__unsafe_unretained UIView *subview in view.subviews) {
if ([NSStringFromClass(subview.class) hasSuffix:classNameSuffix]) {
return subview;
}else {
if ((theView = PSPDFViewWithSuffix(subview, classNameSuffix))) break;
}
}
return theView;
}
- (void)correctSearchDisplayFrames {
// Update search bar frame.
CGRect superviewFrame = self.searchDisplayController.searchBar.superview.frame;
superviewFrame.origin.y = 0.f;
self.searchDisplayController.searchBar.superview.frame = superviewFrame;
// Strech dimming view.
UIView *dimmingView = PSPDFViewWithSuffix(self.view, #"DimmingView");
if (dimmingView) {
CGRect dimmingFrame = dimmingView.superview.frame;
dimmingFrame.origin.y = self.searchDisplayController.searchBar.frame.size.height;
dimmingFrame.size.height = self.view.frame.size.height - dimmingFrame.origin.y;
dimmingView.superview.frame = dimmingFrame;
}
}
- (void)setAllViewsExceptSearchHidden:(BOOL)hidden animated:(BOOL)animated {
[UIView animateWithDuration:animated ? 0.25f : 0.f animations:^{
for (UIView *view in self.tableView.subviews) {
if (view != self.searchDisplayController.searchResultsTableView &&
view != self.searchDisplayController.searchBar) {
view.alpha = hidden ? 0.f : 1.f;
}
}
}];
}
// This fixes UISearchBarController on iOS 7. rdar://14800556
- (void)correctFramesForSearchDisplayControllerBeginSearch:(BOOL)beginSearch {
if (PSPDFIsUIKitFlatMode()) {
[self.navigationController setNavigationBarHidden:beginSearch animated:YES];
dispatch_async(dispatch_get_main_queue(), ^{
[self correctSearchDisplayFrames];
});
[self setAllViewsExceptSearchHidden:beginSearch animated:YES];
[UIView animateWithDuration:0.25f animations:^{
self.searchDisplayController.searchResultsTableView.alpha = beginSearch ? 1.f : 0.f;
}];
}
}
- (void)searchDisplayControllerWillBeginSearch:(UISearchDisplayController *)controller {
[self correctFramesForSearchDisplayControllerBeginSearch:YES];
}
- (void)searchDisplayControllerDidBeginSearch:(UISearchDisplayController *)controller {
[self correctSearchDisplayFrames];
}
- (void)searchDisplayControllerWillEndSearch:(UISearchDisplayController *)controller {
[self correctFramesForSearchDisplayControllerBeginSearch:NO];
}
- (void)searchDisplayController:(UISearchDisplayController *)controller didShowSearchResultsTableView:(UITableView *)tableView {
// HACK: iOS 7 requires a cruel workaround to show the search table view.
if (PSPDFIsUIKitFlatMode()) {
controller.searchResultsTableView.contentInset = UIEdgeInsetsMake(self.searchDisplayController.searchBar.frame.size.height, 0.f, 0.f, 0.f);
}
}
Go to storyboard.
Click on the view controller.
Go to attribute inspector under the ViewController section.
Set the Extend Edges section to be Under Top Bars and Under Opaque Bars.
Make sure to un-check Under Bottom Bars.
Don't know why, my search bar in iOS 7 is leaving a right space. It's ok in iOS 6.
I know it has something to do with the section index, because if I remove it the space disappears, but I don't know how to fix it. Any thoughts?
Embed your UISearchBar in a UIView and then add that as the tableHeaderView. Structuring it that way in a storyboard worked for me. I'm guessing iOS resizes the UISearchBar in the tableHeaderView, but leaves a basic UIView alone (and doesn't bother to look inside it).
You might also want to make the section index transparent, which I did with:
[[UITableView appearance] setSectionIndexBackgroundColor:[UIColor clearColor]];
[[UITableView appearance] setSectionIndexTrackingBackgroundColor:[UIColor clearColor]];
Until a better answer appears, I just manually changed the frame of the search bar like this:
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
CGRect barFrame = self.searchBar.frame;
barFrame.size.width = self.view.bounds.size.width;
self.searchBar.frame = barFrame;
}
I had this same issue with the iPhone 6/ 6Plus when using a SearchDisplayController. (Using Swift)
I tried setting the frame of the search bar but with no luck but i noticed that if i tapped on the textField of the UISearchBar and then cancelled it then it would take on the proper size of the view. I therefore managed to fix the issue by calling the code below in ViewDidLoad of the viewController using the search.
self.searchController.setActive(true, animated: false)
self.searchController.setActive(false, animated: false)
self.contactsTableView.sectionIndexBackgroundColor = [UIColor clearColor];
The reason for that white edge is because your index layer has a white background and is on top of the search bar. This should be sufficient.
Add the search bar inside a UIView put as tableView's header view. Set the tableview's sectionIndexBackgroundColor to clear color because it covers the header.
Tested with iOS 7, 7.1;
Because the table view always leaves 15px on the right for section Indexes View, so you should resize the Seach bar after reloading the table view
First:
self.tblData.sectionIndexBackgroundColor = [UIColor clearColor]; //(iOS >= 7 only)
Cheating time:
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{
[self performSelector:#selector(resizeSearchBar) withObject:nil afterDelay:0.01];
}
- (void) resizeSearchBar
{
CGRect frame = self.searchBar.frame;
if (frame.size.width < self.tblData.frame.size.width) {
frame.size.width = self.tblData.frame.size.width;
}
self.searchBar.frame = frame;
}
- (void) reloadTableData // call it anytime you want to reload table view
{
[self.tblData reloadData];
[self performSelector:#selector(resizeSearchBar) withObject:nil afterDelay:0.01];
}
Suggest
Dont cheat like me, just do the simpler way:
self.searchBar.searchBarStyle = UISearchBarStyleMinimal; // iOS >= 7 only
I also attached a UISearcBar in my application, and nothing is wrong there even my application supports rotation also.
Could you try removing and re creating UISearchBar in storyboard/xib
I added the search bar as a subview of the top-level view instead of the table view. Used autolayout to pin the searchbar to the top guide, and a vertical space constraint of 0 between the search bar and the table view.
The accepted solution with the method viewDidLayoutSubviews makes the screen flicker.
Instead what I did was create a subclass of UISearchBar that simply does this:
FullWidthSearchBar.h:
#interface FullWidthSearchBar : UISearchBar
#end
FullWidthSearchBar.m:
#import "FullWidthSearchBar.h"
#implementation FullWidthSearchBar
- (void)setFrame:(CGRect)frame {
frame.size.width = self.superview.bounds.size.width;
[super setFrame:frame];
}
#end
And then I assigned that class to the search bar on my xib:
The problem is the right white block, so if we change the block color the same as the search bar background, it looks normal.
just
if (IOS7) {
self.tableview.backgroundColor = [UIColor colorWithPatternImage:self.searchBar.backgroundImage];
}