My use case is kind of strange. I'm using my own navigation bar, so I hide the default one with
[self.navigationController setNavigationBarHidden:YES animated:NO];
on viewWillAppear.
Everything works great, but if I have the keyboard open on an active search, and then I go back to a previous UIViewController, then the native navigation bar shows up again, and I end up with 2 navigation bars (my own and the default).
This is how I'm setting up my UISearchController in viewDidLoad:
- (void)viewDidLoad {
[super viewDidLoad];
self.searchController = [[UISearchController alloc] initWithSearchResultsController:nil];
self.searchController.searchResultsUpdater = self;
self.searchController.obscuresBackgroundDuringPresentation = NO;
self.searchController.searchBar.delegate = self;
[self.searchController.searchBar sizeToFit];
self.definesPresentationContext = YES;
self.searchController.hidesNavigationBarDuringPresentation = YES;
self.searchController.searchBar.searchBarStyle = UISearchBarStyleMinimal;
}
I already tried removing the self.definesPresentationContext = YES; (or turning it to NO), but that creates a different issue, which is that when I go back to the previous UIViewController the search bar stays on top of everything else! Until I tap on Cancel. I also tried calling the Cancel button programatically on viewWillDissappear, but that didn't work either...
So I'm running out of options, and that's why I'm here.
Any thoughts?
Using a navigation bar that is not the standard maybe it's not the best idea. (I know customers can sometimes be stubborn, but we should teach them that sometimes standard solutions have a lot of good points, like low maintenance for example, which turns in lower bills for them).
Having said that, as a last resort I may suggest you a quite "strong" approach.
You could subclass the UINavigationController with a custom class, and inside this class you could override the setNavigationBarHidden method like this
- (void)setNavigationBarHidden:(BOOL)hidden animated:(BOOL)animated{
[super setNavigationBarHidden:YES animated:NO];
}
method. This should make the bar hidden all the time.
Still, i'm not a big supported of this kind of solutions, but it may work in your case.
Related
So I have a navigation controller hooked up to a view controller. That obviously provides the default navigation bar. Below that, I have another navigation bar with two buttons. In code I am manually adding the search bar that the UISearchController provides to that navigation bar. Below that, there is another filter view (custom), then a UITableView. Everything seems to work, but when I click search and start typing, the result view covers up the second navigation bar and the search bar, making it sort of useless. I tried playing around with self.searchController.hidesNavigationBarDuringPresentation = NO; but that just hides the main navigation bar. The even more infuriating part is the result view controller is actually leaving space for the second navigation bar, but it's just hidden behind this. It is even possible to see this in the debug view heirachy menu. Here’s the code I'm using for the search controller:
CUSearchResultsTableViewController *results = [self.storyboard instantiateViewControllerWithIdentifier:#"searchResults"];
self.searchController = [[UISearchController alloc] initWithSearchResultsController:results];
self.searchController.searchResultsUpdater = self;
self.searchBarNavItem.titleView = self.searchController.searchBar;
self.searchController.hidesNavigationBarDuringPresentation = NO;
self.searchController.obscuresBackgroundDuringPresentation = NO;
self.searchController.dimsBackgroundDuringPresentation = NO;
self.definesPresentationContext = YES;
self.searchController.delegate = self;
self.searchController.searchBar.delegate = self;
Here is how you can do it:
objective-c
[self.navigationItem setHidesSearchBarWhenScrolling:NO];
Swift
self.navigationItem.hidesSearchBarWhenScrolling = false;
When setting a UISearchController search bar in the navigationItem titleView, the search bar can't be edited.
In my viewDidLoad I am configuring a UISearchController.
self.searchViewController = [self.storyboard instantiateViewControllerWithIdentifier:NSStringFromClass([SearchViewController class])];
self.searchViewController.delegate = self;
self.searchController = [[UISearchController alloc] initWithSearchResultsController:self.searchViewController];
self.searchController.delegate = self;
self.searchController.searchResultsUpdater = self;
self.searchController.searchBar.delegate = self;
self.navigationItem.titleView = self.searchController.searchBar;
I can't tap the search bar. The cursor does not appear and there is no user interaction.
Oddly, if I initialize the UISearchController locally without setting it to a property, then I can edit the search bar, just no delegate callbacks.
self.searchViewController = [self.storyboard instantiateViewControllerWithIdentifier:NSStringFromClass([SearchViewController class])];
self.searchViewController.delegate = self;
UISearchController *searchController = [[UISearchController alloc] initWithSearchResultsController:self.searchViewController];
searchController.delegate = self;
searchController.searchResultsUpdater = self;
searchController.searchBar.delegate = self;
self.navigationItem.titleView = searchController.searchBar;
Another interesting behavior is that the clear button works (if some text is set in the search bar while initializing).
I had the same issue.
Imagine you have FirstViewController and SecondViewController, and booth have a UISearchBar on the titleView.
To fix the problem I had this code to booth UIViewController's.
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
self.definesPresentationContext = true
}
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
self.definesPresentationContext = false
}
I am setting self.definesPresentationContext = YES; in the view controller that presents the view controller in question.
This must be set to self.definesPresentationContext = NO; in viewWillAppear:.
Now the search bar in the presented view controller can be edited.
Set your search bar to navigation title view :
self.navigationItem.titleView = self.searchBarTop;
then just set this view either left/right button of Navigation Bar
UIBarButtonItem *searchBarItem = [[UIBarButtonItem alloc] initWithCustomView:searchBar];
self.navigationItem.rightBarButtonItem = searchBarItem;
I hope this will work for You!!
I have just solved a very similar problem in my app, and thought I'd share the solution in case the existing solutions don't fix it for you.
In my case I had a tab bar application, with my custom controllers in the tabs, embedded in the navigation controllers. The symptoms were exactly the same, the search bar showed in the title area of the navigation bar, but was not interactive.
I have discovered that the problem was that I used my custom subclass of the UITabBarController and I have overriden the viewWillAppear(animated:) method, but forgot to call super.viewWillAppear(animated:) in the implementation. The additional symptom was that when I switched the tabs, the search bar suddenly became interactive and everything worked fine, just the interaction on the initial tab was disabled.
I hope this helps someone.
In your first block of code, you're instantiating with a SearchViewController identifier. In the second, you're using HBSearchViewController. This suggests that there might be another difference in your code besides using / not using an outlet.
When performing a search on a dataset via a UISearchBar, the search results successfully display in the UITableViewController's UITableView. However, when scrolling down through the results, the UITableView's rows visibly appear underneath the UINavigationBar and the simulator's status bar.
This obviously is not the look that I'm going for. Ideally, I would like the UISearchBar to act as the UITableView's header with all search results being contained below the UISearchBar's scope buttons, but my attempts have been unsuccessful.
Below is the Storyboard setup of the relevant UITableViewController and its UITableView's properties.
Below is the relevant code that I am using to setup the UISearchController and its UISearchBar.
BallotTunesSearchTableViewController.h
#interface BallotTunesSearchTableViewController : UITableViewController <UISearchControllerDelegate, UISearchResultsUpdating, UISearchBarDelegate>
BallotTunesSearchTableViewController.m
- (void)viewDidLoad {
[super viewDidLoad];
self.appDelegate = [[UIApplication sharedApplication] delegate];
// Initialize the search controller
self.searchController = [[UISearchController alloc] initWithSearchResultsController:nil];
self.searchController.searchResultsUpdater = self;
self.searchController.dimsBackgroundDuringPresentation = NO;
// Setup the search bar
self.searchController.searchBar.delegate = self;
self.searchController.searchBar.scopeButtonTitles = [NSMutableArray arrayWithObjects:SongScopeName, ArtistScopeName, AlbumScopeName, nil];
self.tableView.tableHeaderView = self.searchController.searchBar;
}
Update: Note that the UITableViewController is embedded in a UINavigationController, and when setting the translucence of the UINavigationBar to NO, the UISearchBar slides off the view along with the UINavigationBar.
Also note that I am not implementing the UISearchBar in Storyboard (however, I may take that route if I can't get my current setup to work).
After several face palms, it all came down to the lack of this line of code:
self.definesPresentationContext = YES;
Setting the presentation context to YES indicates that the view controller's view should be covered when the view controller presents the UISearchController.
My app allows users to import video and perform a few other actions like send an email. When I call the standard code to present these view controllers (example below), the result is that scrollable elements appear underneath the status bar, which is hideous. My own View Controllers do not suffer from this glitch. The problem seems to occur in iOS 7 and 8.
I see many related questions, but most of them address dealing with the status bar in your own view controllers, and I've already done that. My question is about dealing with view controllers I get from the OS.
Any idea what we might be doing (e.g. in storyboard) to cause this? Did I forget or miss an obvious step? It's very ugly and distracting and a fix would be fantastic. I don't see this in any other app.
UIImagePickerController *mediaUI = [[UIImagePickerController alloc] init];
mediaUI.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
// Displays saved movies from the Camera Roll album.
mediaUI.mediaTypes = #[(__bridge NSString *)kUTTypeMovie];
mediaUI.allowsEditing = YES;
mediaUI.delegate = self; //with or without this, the glitch appears
[self presentViewController:mediaUI animated:YES completion:nil];
Here's what it looks like:
The only solution I could find was to remove the status bar. This might be possible via davidisdk's answer to this question, but I did it by subclassing UIImagePicker (and a few other classes) like this:
#interface NoStatusBarImagePickerController : UIImagePickerController
#end
#implementation NoStatusBarImagePickerController
- (BOOL)prefersStatusBarHidden {
return YES;
}
- (UIViewController *)childViewControllerForStatusBarHidden {
return nil;
}
#end
Note:
See accepted answer (not top voted one) for solution as of iOS 4.3.
This question is about a behavior discovered in the iPad keyboard, where it refuses to be dismissed if shown in a modal dialog with a navigation controller.
Basically, if I present the navigation controller with the following line as below:
navigationController.modalPresentationStyle = UIModalPresentationFormSheet;
The keyboard refuses to be dismissed. If I comment out this line, the keyboard goes away fine.
...
I've got two textFields, username and password; username has a Next button and password has a Done button. The keyboard won't go away if I present this in a modal navigation controller.
WORKS
broken *b = [[broken alloc] initWithNibName:#"broken" bundle:nil];
[self.view addSubview:b.view];
DOES NOT WORK
broken *b = [[broken alloc] initWithNibName:#"broken" bundle:nil];
UINavigationController *navigationController =
[[UINavigationController alloc]
initWithRootViewController:b];
navigationController.modalPresentationStyle = UIModalPresentationFormSheet;
navigationController.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:navigationController animated:YES];
[navigationController release];
[b release];
If I remove the navigation controller part and present 'b' as a modal view controller by itself, it works. Is the navigation controller the problem?
WORKS
broken *b = [[broken alloc] initWithNibName:#"broken" bundle:nil];
b.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:b animated:YES];
[b release];
WORKS
broken *b = [[broken alloc] initWithNibName:#"broken" bundle:nil];
UINavigationController *navigationController =
[[UINavigationController alloc]
initWithRootViewController:b];
[self presentModalViewController:navigationController animated:YES];
[navigationController release];
[b release];
This has been classified as "works as intended" by Apple engineers. I filed a bug for this a while back. Their reasoning is that the user is often going to be entering data in a modal form so they are trying to be "helpful" and keep the keyboard visible where ordinarily various transitions within the modal view can cause the keyboard to show/hide repeatedly.
edit: here is the response of an Apple engineer on the developer forums:
Was your view by any chance presented with the UIModalPresentationFormSheet style? To avoid frequent in-and-out animations, the keyboard will sometimes remain on-screen even when there is no first responder. This is not a bug.
This is giving a lot of people problems (myself included) but at the moment there doesn't seem to be a way to work around it.
UPDATE:
In iOS 4.3 and later, you can now implement `-disablesAutomaticKeyboardDismissal' on your view controller to return NO:
- (BOOL)disablesAutomaticKeyboardDismissal {
return NO;
}
This fixes the issue.
Be careful if you are displaying the modal with a UINavigationController. You then have to set the disablesAutomaticKeyboardDismissal on the navigation controller and not on the view controller. You can easily do this with categories.
File: UINavigationController+KeyboardDismiss.h
#import <Foundation/Foundation.h>
#interface UINavigationController (KeyboardDismiss)
- (BOOL)disablesAutomaticKeyboardDismissal;
#end
File: UINavigationController+KeyboardDismiss.m
#import "UINavigationController+KeyboardDismiss.h"
#implementation UINavigationController(KeyboardDismiss)
- (BOOL)disablesAutomaticKeyboardDismissal
{
return NO;
}
#end
Do not forget to import the category in the file where you use the
UINavigationController.
In the view controller that is presented modally, just override disablesAutomaticKeyboardDismissal to return NO:
- (BOOL)disablesAutomaticKeyboardDismissal {
return NO;
}
I solved this by using the UIModalPresentationPageSheet presentation style and resizing it immediately after I present it. Like so:
viewController.modalPresentationStyle = UIModalPresentationPageSheet;
viewController.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:viewController animated:YES];
viewController.view.superview.autoresizingMask =
UIViewAutoresizingFlexibleTopMargin |
UIViewAutoresizingFlexibleBottomMargin;
viewController.view.superview.frame = CGRectMake(
viewController.view.superview.frame.origin.x,
viewController.view.superview.frame.origin.y,
540.0f,
529.0f
);
viewController.view.superview.center = self.view.center;
[viewController release];
If you toggle a different modal display you can get the keyboard to disappear. It's not pretty and it doesn't animate down, but you can get it to go away.
It'd be great if there was a fix, but for now this works. You can wedge it in a category on UIViewController and call it when you want the keyboard gone:
#interface _TempUIVC : UIViewController
#end
#implementation _TempUIVC
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return YES;
}
#end
#implementation UIViewController (Helpers)
- (void)_dismissModalViewController {
[self dismissModalViewControllerAnimated:NO];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardDidHideNotification object:nil];
[self release];
}
- (void)forceKeyboardDismissUsingModalToggle:(BOOL)animated {
[self retain];
_TempUIVC *tuivc = [[_TempUIVC alloc] init];
tuivc.modalPresentationStyle = UIModalPresentationCurrentContext;
[self presentModalViewController:tuivc animated:animated];
if (animated) {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(_dismissModalViewController) name:UIKeyboardDidHideNotification object:nil];
} else
[self _dismissModalViewController];
[tuivc release];
}
#end
Be careful with this though as you viewDidAppear / viewDidDisappear and all those methods get called. Like I said, it's not pretty, but does work.
-Adam
You could also work around this in a universal app by simply checking the idiom and if it's an iPad, don't pop up the keyboard automatically at all and let the user tap whatever they want to edit.
May not be the nicest solution but it's very straightforward and doesn't need any fancy hacks that will break with the next major iOS release :)
Put this code in your viewWillDisappear: method of current controller is another way to fix this:
Class UIKeyboardImpl = NSClassFromString(#"UIKeyboardImpl");
id activeInstance = [UIKeyboardImpl performSelector:#selector(activeInstance)];
[activeInstance performSelector:#selector(dismissKeyboard)];
I found that disablesAutomaticKeyboardDismissal and adding a disablesAutomaticKeyboardDismissal function didn't work for my UITextField in a modal dialog.
The onscreen keyboard just wouldn't go away.
My solution was to disable all text-input controls in my dialog, then re-enable the relevant ones a fraction of a second later.
It seems as though when iOS sees that none of the UITextField controls are enabled, then it does get rid of the keyboard.
I'm sure you have looked at this, but you are sure that your controller class is properly hooked up as the UITextField delegate, right?
maybe don't return NO, but YES. So it can go away.
And you have a textFieldShouldEndEditing returning YES as well?
And why are you firing [nextResponder becomeFirstResponder]?! sorry i see now
I also have a number of UITextViews
which all have their "editable"
property set to FALSE.
May we assume none of them, by any chance, has a tag value of secondField.tag+1? If so, you're telling them to become first responder, instead of resigning the first responder. Maybe put some NSLog() in that if structure.
For those having trouble with UINavigationController, see my answer to a similar question here:
https://stackoverflow.com/a/10507689/321785
Edit:
I consider this an improvement to Miha Hribar's solution (since the decision is taking place where it should), and as opposed to Pascal's comment regarding a category on UIViewController
may be not a perfect solution ,but works
[self.view endEditing:YES];
from wherever your button or gesture is implemented to present modal
Swift 4.1:
extension UINavigationController {
override open var disablesAutomaticKeyboardDismissal: Bool {
return false
}
}