The structure:
View1 (click a button) -> present modally (MyModalView: UITableViewController)
MyModalView has UISearchController embedded. The searchBar of UISearchController is placed in MyModalView.tableView.tableHeaderView.
It's been working fine since iOS 8.0. However on iOS 9, the searchBar disappear when the UISearchController is active. Please take a look at theses pictures below
The modal view:
UISearchController active on iOS 8:
UISearchController active on iOS 9:
The very standard code:
override func viewDidLoad() {
super.viewDidLoad()
// Dynamically create a search controller using anonymous function
self.resultSearchController = ({
let controller = UISearchController(searchResultsController: nil)
controller.searchResultsUpdater = self
controller.dimsBackgroundDuringPresentation = false
controller.searchBar.sizeToFit()
controller.searchBar.delegate = self
self.tableView.tableHeaderView = controller.searchBar
return controller
})()
// Auto sizing row & cell height
self.tableView.estimatedRowHeight = 130
self.tableView.rowHeight = UITableViewAutomaticDimension
self.definesPresentationContext = true
// No footer for better presentation
self.tableView.tableFooterView = UIView.init(frame: CGRectZero)
}
This issue also happens in iOS 9.1 beta...
Any idea / pointer would be deeply appreciated
Cheers.
I'm not sure what exactly is the problem but I 'fixed' it by:
self.searchController.hidesNavigationBarDuringPresentation = NO;
self.definesPresentationContext = NO;
My guess is that UISearchController is doing something funky when it is trying to present as a navigation bar. So, this is a hack but it at least doesn't block the user. The search bar doesn't do the cool animation and cover up the navigation bar.
It seems all of us got the same problem but they were solved in different ways. However none of the suggested answers worked for me :(. Nevertheless thank you all for your time.
I got a solution that solved my problem. It is setting Extend Edges - Under Opaque Bars of my (MyModalView: UITableViewController) to true in the Storyboard using Interface Builder.
In summary:
MyModalView: UITableViewController, in Storyboard using Interface Builder has
Extend Edges:
- Under Top Bars ticked
- Under Bottom Bars ticked
- Under Opaque Bars ticked
I found it's the simulated metrics (top bar) in storyboard that's cause this problem.
In my case, the following lines work, but I still don't know why.
- (void)willPresentSearchController:(UISearchController *)searchController {
// do something before the search controller is presented
self.navigationController.navigationBar.translucent = YES;
}
-(void)willDismissSearchController:(UISearchController *)searchController
{
self.navigationController.navigationBar.translucent = NO;
}
I had to
self.aNavigationController?.extendedLayoutIncludesOpaqueBars = true
I found a similar question here but in my case it was not on the viewDidLoad method. I had to try different views until it worked. Now I can have both a custom navigation bar color and the search bar,
Thanks #wiles duan and #Techprimate
In my case, I fixed this issue by setting:
self.definesPresentationContext = NO;
And implement the following 2 methods in UISearchControllerDelegate
- (void)willPresentSearchController:(UISearchController *)searchController {
// do something before the search controller is presented
self.navigationController.navigationBar.translucent = YES;
}
-(void)willDismissSearchController:(UISearchController *)searchController
{
self.navigationController.navigationBar.translucent = NO;
}
I fixed it in my case by removing
definesPresentationContext = true
I didn't test yet if there are any disadvantages of removing this!
I had the same problem, and when I debugged the UI on Xcode I found that the UISearchBar view was moved to another view and the width was zeroed.
I fixed it by setting definesPresentationContext property of the UISearchController to false, and setting it true for the containing UITableViewController.
I added only one line to your viewDidLoad().
override func viewDidLoad() {
super.viewDidLoad()
// Dynamically create a search controller using anonymous function
self.resultSearchController = ({
let controller = UISearchController(searchResultsController: nil)
controller.searchResultsUpdater = self
controller.dimsBackgroundDuringPresentation = false
controller.definesPresentationContext = false // Disable the presentation controller
controller.searchBar.sizeToFit()
controller.searchBar.delegate = self
self.tableView.tableHeaderView = controller.searchBar
return controller
})()
// Auto sizing row & cell height
self.tableView.estimatedRowHeight = 130
self.tableView.rowHeight = UITableViewAutomaticDimension
self.definesPresentationContext = true // This one remains the same
// No footer for better presentation
self.tableView.tableFooterView = UIView.init(frame: CGRectZero)
}
I don't have a navigation bar in this place of an app. None of other SO posts helped me, so I've fixed it this way:
- (void)layoutSubviews
{
[[[self searchController] searchBar] sizeToFit];
}
Setting the navigationBar permanently to translucent in storyboard solved my problem.
It works
override func viewDidLoad() {
super.viewDidLoad()
self.extendedLayoutIncludesOpaqueBars = !self.navigationController!.navigationBar.translucent
}
If you want to hide you navigation bar, and present search controller full screen, set the following on your navigation bar and search bar won't dissapper:
navigationController?.navigationBar.translucent = true
sc.hidesNavigationBarDuringPresentation = false
does the trick for me
lazy var searchController:UISearchController = {
let sc = UISearchController(searchResultsController: nil)
sc.searchResultsUpdater = self
sc.obscuresBackgroundDuringPresentation = false
sc.searchBar.placeholder = "Search"
sc.hidesNavigationBarDuringPresentation = false
return sc
}()
None of them worked for me, I fixed it using this hack
func position(for bar: UIBarPositioning) -> UIBarPosition {
if UIDevice.current.userInterfaceIdiom == .pad {
return .top
} else {
if iOSVersion <= 9 {
return .top
}
return .topAttached
}
}
Related
I am running into a small problem, I implemented the new iOS 11's style search bar in my app, and I noticed that it disappeared with a slightly different animation from the one in Messages for example. It's faster and less smooth.
Anyone has ever stumble upon this "problem" ?
Here is the code I use :
searchController.searchResultsUpdater = self
searchController.obscuresBackgroundDuringPresentation = false
searchController.searchBar.delegate = self
if #available(iOS 11.0, *) {
navigationItem.searchController = searchController
} else {
tableView.tableHeaderView = searchController.searchBar
searchController.searchBar.barTintColor = .white
searchController.searchBar.layer.borderColor = UIColor.white.cgColor
searchController.searchBar.layer.borderWidth = 1
}
definesPresentationContext = true
searchController.searchBar.placeholder = "all_search".localized
EDIT:
I don't know if it will help you, but I am scrolling at a normal pace .
Thanks
Adding
self.extendedLayoutIncludesOpaqueBars = true
to my viewDidLoad solved the issue, your navigation bar must not be translucent and note that extendedLayoutIncludesOpaqueBars = true is being attributed to my main view which holds the tableview.
This happens when your table view doesn't go all the way to the top of the view. Make sure your table view is "behind" the navigation bar and uses extended edges and extend under opaque edges if your navigation bar is opaque.
Try this, it fixes it for me. I used a different UIViewController as the searchResultsUpdater and just set extendedLayoutIncludesOpaqueBars as true.
searchResultsUpdater.extendedLayoutIncludesOpaqueBars = true
searchController.searchResultsUpdater = searchResultsUpdater
UIView.animate(withDuration: 1, animations: {
//your codes to implement
}, completion: nil)
change withDuration: in seconds
Issue: Searchbar shifts down when presented. Before presented, the bar is right below the navigation bar.
More Info:
The navigation bar is just a UINavigationBar that's manually added to a UIViewController through storyboard
Most importantly, the UIViewController uses UIPresentationController to create that effect where the presented VC is slightly offset from the top and the presenting VC is scaled down and "behind" the presented VC. The shift does not happen if I don't use a UIPresentationController.
searchController.hidesNavigationBarDuringPresentation = NO
Any Ideas?
self.definesPresentationContext = YES;
or
self.edgesForExtendedLayout = UIRectEdgeNone;
On a side not it is probably better to add the searchbar to the header of the tableview.
if #available(iOS 11.0, *) {
navigationItem.searchController = searchController
navigationItem.hidesSearchBarWhenScrolling = false
} else {
tableView.tableHeaderView = searchController.searchBar
}
if you set searchBar to tableHeaderView under iOS 11.0 will cause this question, so you can do it like this. everything will be ok!
I've implemented a 1UISearchController1 for my tableview, with scopes and everything. However it doesn't display correctly when the view loads initially.
There are four different things I see depending on my actions.
This is what I see when the view first appears. I wouldn't expect the scope bar to appear:
Now, if I click on the search bar, I see this:
Next, if I click on the Cancel button again, I see this on the table view:
Finally, if I tap on the search bar again, I see what I would expect when I tapped on the search bar the first time:
And if I cancel one more time, now the search bar displays correctly on the table view:
Here's the relevant part of my TableViewController.
class BaseWineTableViewController: UITableViewController, UITableViewDelegate, UINavigationControllerDelegate, UITableViewDataSource, UISearchResultsUpdating, UISearchBarDelegate, UISearchControllerDelegate {
var resultSearchController = UISearchController()
func ConfigureSearchController() {
self.resultSearchController = UISearchController(searchResultsController: nil)
self.resultSearchController.searchResultsUpdater = self
self.resultSearchController.searchBar.delegate = self
self.resultSearchController.delegate = self
self.resultSearchController.searchBar.showsCancelButton = true
self.resultSearchController.searchBar.autocapitalizationType = .None
self.resultSearchController.searchBar.showsScopeBar = true
self.resultSearchController.searchBar.scopeButtonTitles = ["All", CELLAR_STATUSES.Cellared, CELLAR_STATUSES.Wishlist, CELLAR_STATUSES.Finished]
self.resultSearchController.definesPresentationContext = true
self.resultSearchController.dimsBackgroundDuringPresentation = false
self.tableView.tableHeaderView = resultSearchController.searchBar
}
override func viewDidLoad() {
super.viewDidLoad()
ConfigureSearchController()
}
I figured it out. When I removed this line:
self.resultSearchController.searchBar.showsScopeBar = true
But left in the line that defines the scope titles, it fixed it.
I had the same problem and it seemed to occur only in iOS8. In iOS9 the searchbar did not cover the tableview.
I am not using the scope bar, so the proposed fix was not applicable to me.
When adding the line
[self.searchController.searchBar sizeToFit];
before assigning the searchBar to the tableview, it worked fine in both iOS8 and iOS9 for me.
-(void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
self.searchController = [[UISearchController alloc] initWithSearchResultsController:nil];
self.searchController.searchResultsUpdater = self;
self.searchController.dimsBackgroundDuringPresentation = NO;
self.searchController.searchBar.delegate = self;
self.definesPresentationContext = YES;
[self.searchController.searchBar sizeToFit];
self.tableView.tableHeaderView = self.searchController.searchBar;
}
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:
My UITableViewController is conforming to the new UISearchControllerDelegate and also UISearchResultsUpdating.
Here is my setup code for the search bar:
override func viewDidLoad() {
var searchController = UISearchController(searchResultsController: self)
searchController.searchResultsUpdater = self
self.tableView.tableHeaderView = searchController.searchBar
self.definesPresentationContext = true
}
However, when running this in the simulator there is no search bar in the table header, even though it is specified in the code. I also tried this code in viewWillAppear, but again no search bar was shown.
I was informed by an Apple Engineer that you must give the Search Bar a frame. If you print the frame of the search bar, you will notice it's height is zero. So this is probably a bug in Apple's code.
searchController.searchBar = CGRectMake(0.0, 0.0, 320.0, 44.0)
Edit:
The documentation specifies that you must pass in the View Controller that you want to display the results. To display this in the same View Controller you are in, pass in nil.
var searchController = UISearchController(searchResultsController: nil)