I added SearchBar to my TableView in ViewDidLoad() doing it:
self.searchBar = UISearchController(searchResultsController: nil)
self.searchBar.searchResultsUpdater = self
self.searchBar.dimsBackgroundDuringPresentation = false
self.searchBar.searchBar.sizeToFit()
self.tableView.tableHeaderView = self.searchBar.searchBar
self.tableView.reloadData()
everything works fine, but when I tap on this SearchBar it disappears. It means, I can still typing, and I can see the results but, don't see SearchBar. I implemented UISearchBarDelegate and I have been trying to add
func searchBarTextDidBeginEditing(searchBar: UISearchBar) {
self.navigationController?.navigationBarHidden = false
}
func searchBarTextDidEndEditing(searchBar: UISearchBar) {
self.navigationController?.navigationBarHidden = true
}
but it still doesn't work. Do you have any idea, why this Search Bar disappears?
solution of this problem is (like #sandy sad) write this line of code in viewDidLoad()
self.aNavigationController?.extendedLayoutIncludesOpaqueBars = true
but now I have a new problem it's mean When I select row in TableView and display new VievController, SearchBar doesn't disappear and I see it in new view. Why?
You need to set extendedLayoutIncludesOpaqueBars to true in viewDidLoad().
self.aNavigationController?.extendedLayoutIncludesOpaqueBars = true
Actually the search Bar is not hiding it is just adjusting it width and height according to the text.
Remove this line from your code and it will work fine.
self.searchBar.searchBar.sizeToFit()
Related
This is how I setup my UISearchController (I am using a new controller BackupSearchResultsVC to show the search result)
class BackupViewViewController: UIViewController {
private lazy var searchController: UISearchController = {
let backupSearchResultsVC = BackupSearchResultsVC.instanceFromNib()
backupSearchResultsVC.postInit(nsBackup)
let searchController = UISearchController(searchResultsController: backupSearchResultsVC)
searchController.searchResultsUpdater = self
searchController.obscuresBackgroundDuringPresentation = true
searchController.searchBar.placeholder = "search_notes".localized
searchController.searchBar.delegate = self
return searchController
}()
override func viewDidLoad() {
super.viewDidLoad()
/** Specify that this view controller determines how the search controller is presented.
The search controller should be presented modally and match the physical size of this view controller.
*/
definesPresentationContext = true
navigationItem.searchController = searchController
}
This is the UI before search begins.
Once the search begins (The search bar will move upward to block the title bar when the search text field is in focus), there is a wide gap between the search result (red background) and the search text bar.
Is there any way to avoid the gap, yet able to use a new controller to show the result?
Thanks.
By default, the presentation is full screen and the searchResultsController goes under the nav bar which has the search bar.
I believe this has something to do with the autolayout constraints / background color set up of your view controller set up to be the searchResultsController
For example, I have set up the like this:
class BackupSearchResultsVC: UIViewController, UISearchResultsUpdating
{
let resultsView = UIView()
override func viewDidLoad()
{
super.viewDidLoad()
configureResultsView()
}
private func configureResultsView()
{
resultsView.translatesAutoresizingMaskIntoConstraints = false
resultsView.backgroundColor = .red
view.addSubview(resultsView)
// Add constraints with top anchor of 200
view.addConstraints([
resultsView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
resultsView.topAnchor.constraint(equalTo: view.topAnchor, constant: 200),
resultsView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
resultsView.trailingAnchor.constraint(equalTo: view.trailingAnchor)
])
}
Because of the top anchor, it gives me the same results you see:
If you want to keep the red view's position as it is, add a background color to your view controller's main view
override func viewDidLoad()
{
super.viewDidLoad()
// Give a background color
view.backgroundColor = .white
configureResultsView()
}
Or you could just remove the constraint to the top
// Add constraints without top anchor of 200
view.addConstraints([
resultsView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
resultsView.topAnchor.constraint(equalTo: view.topAnchor),
resultsView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
resultsView.trailingAnchor.constraint(equalTo: view.trailingAnchor)
])
If this still does not work, try backupSearchResultsVC.modalPresentationStyle = .fullScreen during your set up
Give one of these a go and see if it solves your problem
Update based on Mr Cheng's
It seems giving the UIViewController used as the searchResultsController seems to make the UINavigationBar go transparent when active and the full view controller is shown behind:
This is different to what I see running the same code:
This leads me to believe this is iOS related as my device is iOS 14 and the result you see is probably iOS 15.
Digging a bit deeper, I came across this post and this which suggests by default, the bar tint is transparent on iOS 15.
So I believe you might need to set a custom background color for the nav bar in iOS 15 as outlined here perhaps
Here is an image of the issue:
As you can see the text is highlighted (select all) was pressed, however, as you can see the text isn't actually highlighted.
I don't think it matters but this search bar isn't searching local data it's using Algolia.
My class in implementing UISearchBarDelegate the search bar is created in code not interface builder and the only method implemented is searchBarSearchButtonClicked which I can include code for, but I don't think it's needed. Additionally, this VC is inside a navigation controller
Code:
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.rowHeight = UITableViewAutomaticDimension
self.tableView.estimatedRowHeight = 135;
searchBar.placeholder = "Search"
searchBar.delegate = self
navigationItem.titleView = searchBar
tableView.delegate = self
tableView.dataSource = self
self.edgesForExtendedLayout = UIRectEdge()
self.extendedLayoutIncludesOpaqueBars = false
self.automaticallyAdjustsScrollViewInsets = false
}
The effect comes from a white selection color. You can change the selection color by assigning a color to the property tintColor of UISearchBar, or over the appearance for all search bars:
UISearchBar.appearance().tintColor = .black
The white space only appears on iOS 10.
I ran into this problem as well. If you have the vertical scroll indicator enabled, you should be able to see that it's a UIScrollView's inset issue. And seems like it only happens when you use a UITableViewcontroller as the searchResultsController of a UISearchController.
And this extra space is visible at both the top and bottom of the view.
This answer is not pretty, but I'm adding this in for now.
if #available(iOS 10.0, *) {
automaticallyAdjustsScrollViewInsets = false
tableView.contentInset = UIEdgeInsetsMake(64, 0, 44, 0)
}
You're setting a UITableViewController as the UISearchController's searchResultsController but without Autolayout nor a frame.
As you can read in the UISearchController's Quick Help, you can pass it nil if you want to display the search results in the same view controller that displays your searchable content.
So you're code will look okay if you set it like this:
class ViewController: UIViewController {
let searchController = UISearchController(searchResultsController: nil)
override func viewDidLoad() {
super.viewDidLoad()
let tableView = UITableView(frame: view.bounds, style: .plain)
tableView.dataSource = self
view.addSubview(tableView)
searchController.hidesNavigationBarDuringPresentation = false
searchController.dimsBackgroundDuringPresentation = false
navigationItem.titleView = searchController.searchBar
searchController.searchBar.becomeFirstResponder()
searchController.searchBar.text = "⬇️ What is this white space? ⬇️"
}
}
...
Based on #yishus answer here: https://stackoverflow.com/a/39871273/4405051
I ran into this problem as well. If you have the vertical scroll indicator enabled, you should be able to see that it's a UIScrollView's inset issue. And seems like it only happens when you use a UITableViewcontroller as the searchResultsController of a UISearchController.
And this extra space is visible at both the top and bottom of the view.
This answer is not pretty, but I'm adding this in for now.
if #available(iOS 10.0, *) {
automaticallyAdjustsScrollViewInsets = false
tableView.contentInset = UIEdgeInsetsMake(64, 0, 44, 0)
}
I disable vertical scroll indicator but the problem is still there. Also, you can't use #available to check for iOS version. It is used for checking that an API is available or not in an iOS version. So I ended up using this solution:
In search results controller (not the main view controller):
override func viewDidLoad() {
super.viewDidLoad()
if ProcessInfo().isOperatingSystemAtLeast(OperatingSystemVersion(majorVersion: 10,
minorVersion: 0,
patchVersion: 0)) {
automaticallyAdjustsScrollViewInsets = false
}
}
When update search results in main view controller:
func updateSearchResults(for searchController: UISearchController) {
if ProcessInfo().isOperatingSystemAtLeast(OperatingSystemVersion(majorVersion: 10,
minorVersion: 0,
patchVersion: 0)) {
searchResultsController.tableView.contentInset = UIEdgeInsets(top: topLayoutGuide.length,
left: 0,
bottom: bottomLayoutGuide.length,
right: 0)
}
// Filter results here
}
searchResultsController is the controller mentioned above.
If you want to handle orientation, set the searchResultsController.tableView.contentInset again when orientation changed.
Still, there's one more problem, everytime the main view controller appear (switch from another tab bar, pop a view controller,...), updateSearchResults is called. It's pretty bad for performance since I load results asynchronously.
I tried a different approach that seems to have worked for me. For your app's main target, set "Hide Status Bar" to true. At least for me (Xcode 8 GM seed) this actually did not hide the status bar within the app, but seems to have corrected the spacing issue.
Whereas this fixed the issue for me when the phone is vertically oriented, it did not entirely resolve the spacing issue when horizontally oriented. This bit of code also fixes the issue on horizontally aligned screens:
override var prefersStatusBarHidden: Bool {
get {
return UIApplication.shared.isStatusBarHidden
}
}
Of course, setting the prefersStatusBarHidden value to false will entirely disable the status bar, but that may not be a feasible workaround for some developers.
Hope this helps. This is a bit of a hack and I hope Apple resolves this in a future iOS update.
Finally, I found the easiest solution:
automaticallyAdjustsScrollViewInsets = false
🎉😃
I'm trying to add an UISearchController on top of an UITableView (not in it's header) and I created a placeholder view for it in storyboard with a height constraint of 44.
In the normal state it all works fine but when I add some scope button, those overlap my first UITableViewCell and I'm unable to find a solution for this. I tried to re-set the height constraint of my placeholder view but I don't find the right functions to place it and couldn't find the "right" animation so it still looks nice.
My UISearchController looks like this:
override func viewDidLoad() {
super.viewDidLoad()
self.searchController.searchResultsUpdater = self
self.searchController.hidesNavigationBarDuringPresentation = true
self.searchController.dimsBackgroundDuringPresentation = false
self.searchController.searchBar.searchBarStyle = .Default
self.searchController.searchBar.tintColor = UIColor.blueColor()
self.searchController.searchBar.delegate = self
self.searchController.searchBar.scopeButtonTitles = ["Test1", "Test2"]
self.searchController.searchBar.sizeToFit()
self.searchBarViewWrapper.addSubview(self.searchController.searchBar)
}
Here is a solution I found worked well. Add searchBar to tableHeaderView instead of your wrapper view:
searchController.searchBar.scopeButtonTitles = ["Test1", "Test2"]
tableView.tableHeaderView = searchController.searchBar
You can download the project to see in detail
http://www.raywenderlich.com/wp-content/uploads/2015/09/CandySearch.zip
I am updating my app to use iOS 7 and I'm having a problem with a table view. My tab bar is translucent. The problem is when I scroll to the bottom of my table view, part of the last cell is still behind the tab bar. I'd like to have a bit of space between the last cell and the tab bar. I could fix this by using an opaque tab bar instead, but I want to keep it translucent.
Try setting
self.edgesForExtendedLayout = UIRectEdgeNone;
self.extendedLayoutIncludesOpaqueBars = NO;
self.automaticallyAdjustsScrollViewInsets = NO;
Inside the tableview controller
Swift 4.x
let adjustForTabbarInsets: UIEdgeInsets = UIEdgeInsetsMake(0, 0, self.tabBarController!.tabBar.frame.height, 0)
self.yourTableView.contentInset = adjustForTabbarInsets
self.yourTableView.scrollIndicatorInsets = adjustForTabbarInsets
Check the screen shot
Check the under top Bar and Un-checke under Bottom Bar
SWIFT 3
put this inside viewDidLoad of your tableViewController:
self.edgesForExtendedLayout = UIRectEdge()
self.extendedLayoutIncludesOpaqueBars = false
self.automaticallyAdjustsScrollViewInsets = false
Swift 3.0
This is what worked for me. In your Custom ViewController:
override func viewDidLoad() {
super.viewDidLoad()
let adjustForTabbarInsets: UIEdgeInsets = UIEdgeInsetsMake(self.tabBarController!.tabBar.frame.height, 0, 0, 0);
//Where tableview is the IBOutlet for your storyboard tableview.
self.tableView.contentInset = adjustForTabbarInsets;
self.tableView.scrollIndicatorInsets = adjustForTabbarInsets;
}
Not to sure I like the solution but it works for me.
With iOS 11 I have no issue, I simply use the following in viewDidLoad():
self.collectionView.bottomAnchor.constraint(self.view.safeAreaLayoutGuide.bottomAnchor).isActive = true
However on iOS 10 I need to hack my way like this:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let tabBarHeight: CGFloat = (self.parent?.tabBarController?.tabBar.frame.size.height)!
if #available(iOS 11.0, *) {
} else {
self.collectionView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: -tabBarHeight).isActive = true
}
}
This is working for me
override func viewDidLoad() {
self.edgesForExtendedLayout = UIRectEdge()
self.extendedLayoutIncludesOpaqueBars = false
}
If any view shows behind a UITabBar you can grab the bottomLayoutGuide and make adjustments at runtime. What I do is have a BaseViewController that all my view controllers inherit from. Then if the tab bar is visible we adjust the view like so:
import UIKit
class BaseVC: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidLayoutSubviews() {
//Ensures that views are not underneath the tab bar
if tabBarController?.tabBar.hidden == false {
var viewBounds = self.view.bounds;
var bottomBarOffset = self.bottomLayoutGuide.length;
self.view.frame = CGRectMake(0, 0, viewBounds.width, viewBounds.height - bottomBarOffset)
}
}
}
Since I don't use storyboards (where you can click a checkbox in IB to fix this problem), this has been the best solution I have found.
It is really hard to resolve the issue without detail information or actual codes. I have similar issue of tabview behind UItabBar in my project. The solutions offered here do not work in my case. After exploring my codes, I found a solution for my case.
Here is brief explanation of my case. I have a UItabBar in main view with two tab buttons. In one tab view, there is table view. If user taps on a row, a detail view is presented by using navigation controller. In the detail view, the tab bar is hidden, and a toolbar is showing at the bottom.
In order to bring tab bar back and hide the toolbar when the main view is brought back, I have to explicitly show tab bar and hide toolbar in the event of viewWillAppear:
class myMainViewController: UITableViewController {
private var tabBarHidden: Bool? = {
didSet {
self.tabBarController?.tabBar.isHidden = tabBarIsHidden ?? true
}
}
private var toolBarIsHidden: Bool? {
didSet {
let hidden = toolBarIsHidden ?? true
self.navigationController?.toolbar.isHidden = hidden
self.navigationController?.setToolbarHidden(hidden, animated: true)
}
}
...
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.tabBarIsHidden = false
self.toolBarIsHidden = true
}
...
}
I finally realize that the visibility of bar at the bottom is set in the event of viewWillAppear. At that time, the tableView or scroll view's content insets are set already based on no bar at the bottom. That's why my tableView is behind the bottom bar.
The solution I found is to reset content insets in the event of viewDidAppear:
override func viewDidAppear(_ animated: Bool) {
// In the event of viewWillAppear, visibilities of tool bar and tab bar are set or changed,
// The following codes resets scroll view's content insets for tableview
let topInset = self.navigationController!.navigationBar.frame.origin.y +
self.navigationController!.navigationBar.frame.height
let adjustForTabbarInsets: UIEdgeInsets = UIEdgeInsetsMake(
topInset, 0,
self.tabBarController!.tabBar.frame.height, 0)
self.tableView.contentInset = adjustForTabbarInsets
self.tableView.scrollIndicatorInsets = adjustForTabbarInsets
}
The best approch would be to Embed TabBarController to your ViewController (Editor -> Embed In -> TabBar Controller)and set the bottom of the tableview to be bottom of safe area of viewcontroller. The other ways wont be as perfect as this one.
You need to adjust the height of the table view. Just leave 49px at the bottom, as the tabbar height is 49 px. Adjust the height of table view so that it leaves 49px space below it.