I have navigation bar with search bar (UISearchController)
I have left bar button icon that when clicked shows this search controller by assigning it to navigationItem like so:
if navigationItem.searchController != nil {
navigationItem.searchController = nil
navigationController?.view.setNeedsLayout()
navigationController?.view.layoutIfNeeded()
} else {
navigationItem.searchController = searchController
navigationController?.view.setNeedsLayout()
navigationController?.view.layoutIfNeeded()
searchController.searchBar.becomeFirstResponder()
}
It works but then if on cancel button touch I try to hide search bar then I have view controller dismissed and black screen appears (no view controllers)
extension SearchableMenuViewController : UISearchBarDelegate {
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
guard #available(iOS 11.0, *) else { return }
guard !isAlwaysVisible else { return }
if #available(iOS 13.0, *) {
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
self.navigationItem.searchController = nil
self.navigationController?.view.setNeedsLayout()
self.navigationController?.view.layoutIfNeeded()
}
} else {
navigationItem.searchController = nil
navigationController?.view.setNeedsLayout()
navigationController?.view.layoutIfNeeded()
}
}
I have tried to add delay cause not removing this searchcontroller from navigationItem animates it to extended navigation bar with Title + Search Controller and then tapping Search icon properly hides search controller. So the problem is I think removing search controller while it is animating to extended navigation bar
Super lame haxx that will solve your issue temporarily:
func didDismissSearchController(_ searchController: UISearchController) {
if #available(iOS 13, *) {
navigationItem.searchController = nil
self.navigationController?.view.setNeedsLayout()
self.navigationController?.view.layoutSubviews()
let view = UIView()
self.navigationController?.navigationBar.insertSubview(view, at: 1)
view.removeFromSuperview()
}
}
Related
I am adding in a UISearchController in my navigation controller and when I do, the entire navigation bar goes black, instead of the color I want. It does go to the desired color when the I click on the search bar however.
Before calling setupSearch()
After calling setupSearch()
class SearchViewController: UIViewController, UISearchBarDelegate, UISearchControllerDelegate {
let searchController = UISearchController(searchResultsController: nil)
override func viewDidLoad() {
super.viewDidLoad()
delegate()
create()
setupNav()
setupSearch()
}
func delegate() {
searchController.delegate = self
searchController.searchBar.delegate = self
}
func create() {
definesPresentationContext = true
view.backgroundColor = .backgroundColor()
}
func setupNav() {
guard let navController = navigationController else { return }
navController.navigationBar.prefersLargeTitles = false
navController.navigationBar.barTintColor = .secondaryColor()
navController.navigationBar.isTranslucent = false
navController.navigationBar.topItem?.title = "Browse"
navController.navigationBar.titleTextAttributes = [.font: UIFont.customHeaderFont(size: 23), .foregroundColor: UIColor.textColor()]
}
func setupSearch() {
let searchBar = searchController.searchBar
searchBar.backgroundImage = UIImage()
searchController.extendedLayoutIncludesOpaqueBars = true
navigationItem.searchController = searchController
}
}
if you give color to barTintColor and set isTranslucent to false,setting navigationItem.searchController will cause the bar to turn black
Solution 1, if you want to always display the searchbar
navigationItem.hidesSearchBarWhenScrolling = false
Solution 2, set navigationController backgroundColor
navigationController?.view.backgroundColor = .red
Change the background color of the View (outermost) that contains the safeArea, that works for me. The reason that you are seeing a black color is that you did not assign any color to the View background so the default one is assigned
I am a bit stuck here. I have a search bar embedded in my navigation bar as the title which I want.
However next I would like to navigate to another view controller containing a UIsearchcontroller in a collection view, and when cancel is tapped the user is brought back to the previous view. (Very similar to Facebook or Instagrams use of the search bar controller)
Is there anyway I can use the search bar as a button to navigate to the next view controller that handles all of the search functions?
This is what I have so far for the search bar.
func setUpNavBar() {
let searchBar = UISearchBar()
searchBar.sizeToFit()
searchBar.searchBarStyle = .minimal
searchBar.placeholder = "Search by username"
searchBar.tintColor = UIColor.lightGray
searchBar.barTintColor = UIColor.lightGray
navigationItem.titleView = searchBar
searchBar.isTranslucent = true
}
Edit
Just to add on to the answer below, if anyone comes across this and you want to get the effect that treats the search bar as a button use
func searchBarShouldBeginEditing(_ searchBar: UISearchBar) -> Bool {
self.present(UINavigationController(rootViewController: SearchBarController()), animated: true, completion: nil)
searchBar.setShowsCancelButton(false, animated: true)
return false
}
This way, when you click the search bar, you do not get the effect that shows the search bar active,( if that makes sense). When you tap the search bar, there is no effect, it just takes you to the (real) search view controller then the search bar is active and the keyboard is displayed with a cancel button. I hope that makes sense, if anyone wants an example go to Instagram, FB, or Pinterest and tap on their search bar or search icon and you'll see.
Add UISearchBarDelegate in first view controller. And in searchBarTextDidBeginEditing method present the second view controller.
//ViewController.swift
class ViewController: UIViewController, UISearchBarDelegate {
override func viewDidLoad() {
super.viewDidLoad()
setUpNavBar()
}
func setUpNavBar() {
let searchBar = UISearchBar()
searchBar.delegate = self
searchBar.sizeToFit()
searchBar.searchBarStyle = .minimal
searchBar.placeholder = "Search by username"
searchBar.tintColor = UIColor.lightGray
searchBar.barTintColor = UIColor.lightGray
navigationItem.titleView = searchBar
searchBar.isTranslucent = true
}
func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) {
self.present(UINavigationController(rootViewController: SearchViewController()), animated: false, completion: nil)
}
}
//SearchViewController.swift
class SearchViewController: UIViewController {
let searchBar = UISearchBar()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
setUpNavBar()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
searchBar.becomeFirstResponder()
}
func setUpNavBar() {
searchBar.sizeToFit()
searchBar.searchBarStyle = .minimal
searchBar.placeholder = "Search by "
searchBar.tintColor = UIColor.lightGray
searchBar.barTintColor = UIColor.lightGray
navigationItem.titleView = searchBar
searchBar.isTranslucent = true
}
}
I want to hide navigation bar after a tap
navigationController?.hidesBarsOnTap = true
The navigationBar hides properly after a tap
But after adding a searchController (code below)
let searchController = UISearchController(searchResultsController: nil)
navigationItem.searchController = searchController
My view (cyan color) could not extend correctly
And I also tried rotated it. The search bar appears.
Finally found a solution
override func viewDidLoad() {
super.viewDidLoad()
navigationController?.barHideOnTapGestureRecognizer.addTarget(self, action: #selector(barHideAction(_:)))
let searchController = UISearchController(searchResultsController: nil)
navigationItem.searchController = searchController
navigationItem.hidesSearchBarWhenScrolling = false
navigationController?.hidesBarsOnTap = true
}
#objc func barHideAction(_ guesture: UITapGestureRecognizer) {
updateFrame()
}
func updateFrame() {
if let nc = navigationController {
let isHidden = nc.isNavigationBarHidden
searchController.searchBar.superview?.isHidden = isHidden
if isHidden {
self.additionalSafeAreaInsets.top = -64 // fixed by a magic num
}
else {
self.additionalSafeAreaInsets.top = 0
}
}
}
example code
So i have a searchcontroller on my navigationItem.
// View controller
var searchController = UISearchController(searchResultsController: nil)
override func viewWillAppear(_ animated: Bool) {
tableView.reloadData()
if #available(iOS 11.0, *) {
navigationController?.navigationBar.prefersLargeTitles = true
}
if employeeSearchList.count > 10 {
if #available(iOS 11.0, *) {
navigationItem.searchController = searchController
} else {
tableView.tableHeaderView = searchController.searchBar
}
} else {
if #available(iOS 11.0, *) {
let search = UISearchController(searchResultsController: nil)
navigationItem.searchController = search
navigationItem.searchController = nil
} else {
tableView.tableHeaderView = nil
}
}
UIView.animate(withDuration: 0.50, animations: {
self.view.layoutIfNeeded()
})
}
This piece of code runs perfectly. If the count is less than 10, the search controller is set, otherwise it is set to an empty search controller and then set to nil, so that it disappears from the UI.
func viewDidLoad() {
.
.
.
searchController.searchResultsUpdater = self
searchController.dimsBackgroundDuringPresentation = false
searchController.searchBar.tintColor = tintColor
// Table view editing delegate -> delete operation
if employeeSearchList.count < 10 {
if #available(iOS 11.0, *) {
let search = UISearchController(searchResultsController: nil)
navigationItem.searchController = search
navigationItem.searchController = nil
} else {
tableView.tableHeaderView = nil
}
UIView.animate(withDuration: 0.50, animations: {
tableView.reloadData()
self.view.layoutIfNeeded()
})
}
.
.
.
}
Now my problem is, when i present a view controller above this one and then dismiss it, the viewWillAppear executes fine but the search controller doesn't show up. But if i push the view controller and come back, it shows up.
What are the main difference between the 2 operations ? (push/pop & present/dismiss)
It should be:
navigationController.navigationItem.searchController = searchController
instead of:
navigationItem.searchController = searchController
The latter will only take effect the next time navigationController is refreshed/loaded/whatever the appropriate term is?
When you present a view controller form other, you are presenting a new viewController hierarchy, I mean, you are outside of the previous navigation controller. If you push a new controller from the navigation controller, you are adding this to the stack, and the navigation bar will be presented.
search controller with tableview is working fine. I have to set the position and hide/show the search controller it's working at the table view header, when tableview scrolling after it's not showing at first time click search bar button at navigation bar. when I double click search button search bar controller is showing. I have to show the search controller with single click of search button after tableview scrolling
this is code of search controller
let searchController = UISearchController(searchResultsController: nil)
override func viewDidLoad() {
super.viewDidLoad()
{
tableView.tableHeaderView = nil
searchController.searchResultsUpdater = self
searchController.hidesNavigationBarDuringPresentation = false
searchController.dimsBackgroundDuringPresentation = false
tableView.tableHeaderView = searchController.searchBar
searchController.delegate = self
searchController.searchBar.sizeToFit()
self.searchController.searchBar.delegate = self
tableView.tableHeaderView = nil
}
func updateSearchResults(for searchController: UISearchController) {
_ = kidsData
let searchToSearch = searchController.searchBar.text
if(searchToSearch == "")
{
self.kidsData = self.KidsDataDuplicate
}
else{
self.kidsData.removeAll()
let itemsarray = self.KidsDataDuplicate
var forkidsinArray = \[String\]()
for Kids in itemsarray {
forkidsinArray.append(Kids.name)
if(Kids.name.range(of: searchToSearch!, options: .caseInsensitive) != nil)
{
self.kidsData.append(Kids)
}
}
}
self.tableView.reloadData()
}
hide and unhide code
var launchBool: Bool = false {
didSet {
if launchBool == true {
tableView.tableHeaderView = searchController.searchBar
let indexPath = IndexPath(row: 0, section: 0)
self.tableView.scrollToRow(at: indexPath, at: .middle, animated: true)
} else {
tableView.tableHeaderView = nil
myInt = 0
}
}
}
#IBAction func NAVSearchButton(_ sender: UIBarButtonItem) {
launchBool = !launchBool
}
after tableview scrolling when I click of search button at navigation search bar controller is not showing when I double click the search button its showing how to fix search controller with tableview when scrolling pls help me
If I understand it correctly then add seperate UISearchbar inside attribute inspector after the navigation item and then add below UITableView. Pinned the constraints. Now your search bar will be always visible. In your case search bar is the set to table header view. So that when you were scrolling search bar is also getting scrolled