I am using UISearchController in a Large header style. But when I push to the next view controller and coming back to the same controller again my UIView hides by navigation bar.
See Video
https://drive.google.com/open?id=1tb5Eeni-79uomBGl1GPEcESDSYOfrZ7v
I can't reproduce your error, but I'll post my test code, as it can help you.
I suggest that you write this code in a BaseViewController: UIViewController and extend it in your ViewControllers for define Large Titles:
self.navigationController?.navigationBar.prefersLargeTitles = true
Define your UISearchController and set your Instance.
var resultSearchController: UISearchController!
resultSearchController = UISearchController(searchResultsController: nil)
resultSearchController.searchResultsUpdater = self
tableView.tableHeaderView = resultSearchController.searchBar
Finally, you can try to close the Search keyboard when the screen disappears, like this:
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
resultSearchController.searchBar.endEditing(true)
}
Hope this helps.
Adding following line in viewwillappear resolves my issue
extendedLayoutIncludesOpaqueBars = true
Related
I have a UINavigationController with default value of it's navigationBar.prefersLargeTitles = true .
I am switching that to false when I push into a new scene lets call it (DetailsViewController), by changing it into the viewWillDisappear .
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
navigationController?.navigationBar.prefersLargeTitles = false
}
Now in DetailsViewController I am using willMove(to parent:) .
override func willMove(toParent parent: UIViewController?) {
navigationController?.navigationBar.prefersLargeTitles = true
}
To transition back to large titles .
Observe the attached snapshots of how iOS 13 doing it with how iOS 12 is doing it, considering iOS 12 is the correct behavior.
iOS 13 :
iOS 12 :
What you're doing was always wrong. You should set prefersLargeTitles to true once for the navigation bar and never touch it again.
The way to change what each view controller does about large titles as it appears is that that view controller sets its own navigationItem (in its viewDidLoad) to have the desired largeTitleDisplayMode. So if the first v.c. has .always and the second has .never everything will be smooth.
Swift 5, Xcode 13:
UIViewController(1) + UINavigationController:
override func viewDidLoad() {
super.viewDidLoad()
navigationController?.navigationBar.prefersLargeTitles = true
navigationItem.title = "Your title here"
}
UIViewController(2 - "i.e.: detailsViewController"):
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.largeTitleDisplayMode = .never
navigationItem.title = "Your title here"
}
It works like a charm!
I had the same issue and had to place a NavigationItem on the second ViewController's storyboard. My NavigationItem was being created automatically by the segue and its prefersLargeTitle in the viewDidLoad() was not finished creating before the view appeared. Adding a NavigationItem to the storyboard fixed this issue and allowed me to set the prefersLargeTitle in the storyboard's properties menu.
In my case this problem was occurring during a segue to a view controller which is a child of a UITabBarController. Setting largeTitleDisplayMode on the child view controller was not enough to fix this bug.
I have solved the issue by adding a navigation item to the UITabBarController scene and setting largeTitleDisplayMode as .never there.
I solved this problem like this:
override func viewWillDisappear(_ animated: Bool) {
title = ""
}
All ingenious is simple))
final class CustomHosting<Content: View>: UIHostingController<Content> {
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationItem.largeTitleDisplayMode = .never
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
self.navigationItem.largeTitleDisplayMode = .automatic
}
}
I fixed same problem like this :) My problem is presenting SUI Detail Collection view from UIKit Collection, and had some jumping while navigation title changing.
I'm trying to implement a UISearchController with it's searchBar inside the navigationBar of a UINavigationController. This navigationBar has translucent and all Extend Edges turned off in Interface Builder.
The setup is quite simple:
ViewControllerWithSearchController is embedded in a UINavigationController. My viewController code looks like this:
class ViewControllerWithSearchController: UIViewController, UISearchControllerDelegate, UISearchResultsUpdating, UISearchBarDelegate {
var searchController: UISearchController!
override func viewDidLoad() {
super.viewDidLoad()
definesPresentationContext = true
initializeSearchController()
}
// MARK: - Search
func initializeSearchController() {
// Create and configure the UISearchController
searchController = UISearchController(searchResultsController: nil)
searchController.delegate = self
searchController.searchResultsUpdater = self
searchController.dimsBackgroundDuringPresentation = false
searchController.searchBar.delegate = self
searchController.searchBar.sizeToFit()
searchController.hidesNavigationBarDuringPresentation = false
navigationItem.titleView = searchController.searchBar
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
// Check the frame of the view
print(view.frame)
}
func updateSearchResultsForSearchController(searchController: UISearchController) {
// Some code here
}
}
When I run this, it works. The frame of the ViewController's view on initial run is (0.0, 64.0, 375.0, 603.0)
This viewController contains a button which pushes a generic viewController onto the stack. When I pop this viewController, the frame of the ViewControllerWithSearchController is unchanged (correct behavior).
However, when I activate the searchBar and then push and pop the second UIViewController (while the searchBar is active), the frame of ViewControllerWithSearchController gets set to (0.0, 0.0, 375.0, 667.0). The view gets extended below the navigationbar, even though this behavior is turned off.
All involved ViewControllers have their Extend Edges options turned off in InterfaceBuilder.
For now, I've implemented a workaround by turning on all the Extend Edges options. This creates a bit of overhead, so I'd rather not do it. Is there a better solution to solve this problem?
I've attached a sample project to illustrate the problem.
Sample project
I've added a UISearchController to my application and set it's searchBar to the titleView of my navigationItem.
This works but I am seeing the cancel button despite having set showsCancelButton to false.
searchController = UISearchController(searchResultsController: searchResultsController)
searchController.searchResultsUpdater = searchResultsUpdater
// Configure the searchBar
searchController.searchBar.placeholder = "Find Friends..."
searchController.searchBar.sizeToFit()
searchController.searchBar.showsCancelButton = false
self.definesPresentationContext = true
navigationItem.titleView = searchController.searchBar
I agree, it seems like a bug. The problem is that the searchController keeps resetting the showsCancelButton property of the searchBar. I found a solution that involves:
subclassing UISearchBar to ignore setShowsCancelButton.
to make the searchController use that subclass, you have to subclass UISearchController.
And then you find that the searchBar is not triggering the search controller's delegate methods, so you have to trigger them separately...
Convoluted, but it seems to do the trick. You can find the full answer here.
This appears to be a bug in iOS. The same behavior I've described can be seen in the example project supplied by Apple
https://developer.apple.com/library/ios/samplecode/TableSearch_UISearchController/Introduction/Intro.html
The documentation states that the default for this is NO but this doesn't seem to be the case. Setting showsCancelButton to NO seems to have no effect.
I have filed a radar for this and am waiting to hear back.
Easy solution in Swift3 - we need to make CustomSearchBar without cancel button and then override the corresponding property in new CustomSearchController:
class CustomSearchBar: UISearchBar {
override func setShowsCancelButton(_ showsCancelButton: Bool, animated: Bool) {
super.setShowsCancelButton(false, animated: false)
}}
class CustomSearchController: UISearchController {
lazy var _searchBar: CustomSearchBar = {
[unowned self] in
let customSearchBar = CustomSearchBar(frame: CGRect.zero)
return customSearchBar
}()
override var searchBar: UISearchBar {
get {
return _searchBar
}
}}
In MyViewController I initialize and configure searchController using this new custom subclass:
var mySearchController: UISearchController = ({
// Display search results in a separate view controller
// let storyBoard = UIStoryboard(name: "Main", bundle: Bundle.main)
// let alternateController = storyBoard.instantiateViewController(withIdentifier: "aTV") as! AlternateTableViewController
// let controller = UISearchController(searchResultsController: alternateController)
let controller = CustomSearchController(searchResultsController: nil)
controller.searchBar.placeholder = NSLocalizedString("Enter keyword (e.g. iceland)", comment: "")
controller.hidesNavigationBarDuringPresentation = false
controller.dimsBackgroundDuringPresentation = false
controller.searchBar.searchBarStyle = .minimal
controller.searchBar.sizeToFit()
return controller
})()
I had to correct by putting in a little hack...
Setting the alpha to 0.0 on viewDidLoad because he screen will flash.
Before you ask...willPresentSearchController will not work.
extension GDSearchTableViewController: UISearchControllerDelegate {
func didPresentSearchController(searchController: UISearchController) {
searchController.searchBar.setShowsCancelButton(false, animated: false)
searchController.searchBar.becomeFirstResponder()
UIView.animateWithDuration(0.1) { () -> Void in
self.view.alpha = 1.0
searchController.searchBar.alpha = 1.0
}
}
}
We wanted the search bar to have no Cancel button initially, but have it appear when the user tapped in the search bar.
Then we wanted the Cancel button to disappear if user tapped Cancel, or otherwise the search bar lost first responder.
What finally worked for me:
On create:
searchBar.showsCancelButton = NO;
We use a subclass of UISearchBar and override searchBarShouldBeginEditing thusly:
-(BOOL)searchBarShouldBeginEditing:(UISearchBar*)searchBar {
self.showsCancelButton = YES;
return YES;
}
We also override resignFirstReponder (in the UISearchBar subclass) thusly:
-(BOOL)resignFirstResponder
{
self.showsCancelButton = NO;
return [super resignFirstResponder];
}
I would also add
searchController.hidesNavigationBarDuringPresentation = false
searchController.delegate = self
searchController.searchBar.delegate = self
See if assigning those delegates will help.
I try to help you man but I'm not sure that I find the real problem.
According to Apple Documentation:
showsCancelButton
boolean property that indicating whether the cancel button is
displayed
But for hide the cancel button maybe you should use:
setShowsCancelButton(_:animated:)
I hope that can be helpful.
You can subclass UISearchBar and override method layoutSubviews
super.layoutSubviews()
self.showsCancelButton = false
my solution was to set the attribute every time anew when I used the searchcontroller respectively its searchbar. I initialized the searchcontroller lazily without setting the attribute and then did
searchController.searchBar.showsCancelButton = false
every time before search began.
You could do this in the UISearchControllerDelegate methods i.e...
This worked for me (iOS 10):
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
self.searchController.searchBar.showsCancelButton = NO;
}
It may be helpful to note that this has changed on iOS 13 and quote Apple's documentation on showsCancelButton, currently only available on UISearchBar.h and not on developer.apple.com
/* New behavior on iOS 13.
If the search bar is owned by a UISearchController, then using the setter
for this property (as well as -setShowsCancelButton:animated:) will implicitly
set the UISearchController's automaticallyShowsCancelButton property to NO.
*/
automaticallyShowsCancelButton has been introduced on iOS 13.0 and should clarify what #pbasdf had already pointed out in his answer: that the buggy behavior is something intrinsic to UISearchController.
What about setting it with [searchBar setShowsCancelButton:NO animated:NO];
https://developer.apple.com/library/ios/documentation/UIKit/Reference/UISearchBar_Class/#//apple_ref/occ/instm/UISearchBar/setShowsCancelButton:animated:
So, I am currently trying to replace the depricated searchDisplayController in one of my projects with UISearchController and I am running into this problem.
If there are no results in the search (the UITableView is empty) the whole ViewController is dismissed. This does not happen when the search results are not empty. I wan't to make it clear I am not using a UITableViewController. Instead I have a regular VC with a UITableView in it.
Here is some of my code:
var resultSearchController = UISearchController()
override func viewDidLoad() {
super.viewDidLoad()
self.resultSearchController = ({
let controller = UISearchController(searchResultsController: nil)
controller.searchResultsUpdater = self
controller.dimsBackgroundDuringPresentation = false
controller.searchBar.sizeToFit()
controller.delegate = self
controller.searchBar.delegate = self
self.studentTable.tableHeaderView = controller.searchBar
return controller
})()
....
}
Now, if I add this function to the equation the cancel button always dismisses the VC.
func searchBarCancelButtonClicked(searchBar: UISearchBar) {
resultSearchController.active = false
}
So why exactly does setting the searchController.active = false dismiss the VC? Is it because it is using the same UITableView as the VC? I believe that the old searchDisplayController would just display a UITableView over the one being used. If this is the case is there a way to override the dismissVC?
this is also Happening to me. The Way I Solve it is by Replacing:
resultSearchController.active = false
with
resultSearchController.searchBar.text = ""
resultSearchController.searchBar.resignFirstResponder()
I Hope this helps you :-)
2018 Just wanna share the fruits of my 1-2 hours debugging.
I had multiple issues with using UISearchController with UITabBarController, namely:
This one, this very question of the OP. Hitting cancel button dismisses the screen that is presenting the searchController.
The tab (or the screen) becomes black, Tab Bar and UISearchController giving black screen
Using UISearchController inside the title view of the navigation bar of UINavigationController in both iOS 10, 11, and 12, like this questions. UISearchBar increases navigation bar height in iOS 11
And for the solution for #3, since we're already here: https://stackoverflow.com/a/53264329/3231194
Finally, the ONLY solution that I have been seeing all this time is adding this code:
self.definesPresentationContext = true
The issue is that I was putting this in a wrong function.
Remember, that solution solved the #1, and #2 problem that I had. Nothing more, nothing less.
Where to add that? Inside the viewDidAppear. That's it!
I have a problem with a UISearchBar. When ill search some Text in combination with an UITableView, and ill click on one result Cell, the UISearchBar is still visible in the next View Controller. If ill go back (with Segues) - the UISearchbar is still there (with the Keyword)
So after ill click on one result, ill get (in the next View Controller):
Ill use it this way:
class ...: UITableViewController, UISearchResultsUpdating {
var filterSearchController = UISearchController(searchResultsController: nil)
override func viewDidLoad() {
super.viewDidLoad()
filterSearchController.searchResultsUpdater = self
filterSearchController.hidesNavigationBarDuringPresentation = false
filterSearchController.dimsBackgroundDuringPresentation = false
filterSearchController.searchBar.searchBarStyle = .Minimal
filterSearchController.searchBar.sizeToFit()
self.tableView.tableHeaderView = filterSearchController.searchBar
Any ideas what could be a problem?
You need to dismiss the UISearchController yourself before transitioning to the next view controller with:
filterSearchController.active = false