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
}
}
Related
This is the situation:
The root view controller of a UINavigationController has a search bar. When a user searches something, they can tap on a cell that pushes a new view controller onto the navigation controller with some info. When this happens, the intended behavior is for the user to be able to go back and still see the keyboard open with the most recent search query populated within the search bar.
However, there is also a situation in which the user may tap on a tab bar that should pop all the view controllers off the navigation controller except the root controller. In that case, I would like to resign the search bar textField's first responder before the root controller is presented. If I try to do this in viewWillAppear(_:), resignFirstResponder() fails.
Is this possible? I've tried using delegation and notifications but nothing seems to work.
As #Sweeper said, endEditing worked for me, but resignFirstResponder didn't. I executed in viewWillAppear:
import PlaygroundSupport
import UIKit
class ViewController: UIViewController {
let button = UIButton()
let searchBar = UISearchBar()
override func viewDidLoad() {
super.viewDidLoad()
button.setTitle("Button", for: .normal)
button.tag = 1
button.addTarget(self, action: #selector(buttonPressed), for: .touchUpInside)
button.backgroundColor = .black
self.view.addSubview(button)
button.translatesAutoresizingMaskIntoConstraints = false
self.navigationItem.titleView = searchBar
NSLayoutConstraint.activate([
button.centerYAnchor.constraint(equalTo: self.view.centerYAnchor),
button.centerXAnchor.constraint(equalTo: self.view.centerXAnchor),
button.widthAnchor.constraint(equalToConstant: 200),
button.heightAnchor.constraint(equalToConstant: 100),
])
}
#objc func buttonPressed(sender: UIButton) {
if case let tag = sender.tag, tag == 1 {
let secondVC = SecondVC()
self.navigationController?.pushViewController(secondVC, animated: true)
}
}
}
class SecondVC: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = .yellow
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.view.endEditing(true)
}
}
let navVC = UINavigationController(rootViewController: ViewController())
PlaygroundPage.current.needsIndefiniteExecution = true
PlaygroundPage.current.liveView = navVC
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 attempting to mimic the search bar behavior below in iOS Safari on my current app:
I mainly want the navigation bar to scroll up to a very small version when the user scrolls on the content and comes back when they scroll back up.
I've tried using scrollViewDidScroll but I cannot seem to get it to mimic that behavior. I'm unsure if I am adding the search bar correctly to the navigation bar.
let searchBar: UISearchBar = {
let sb = UISearchBar()
sb.autocapitalizationType = .none
sb.autocorrectionType = .no
sb.keyboardAppearance = UIKeyboardAppearance.default
sb.placeholder = "Search"
return sb
}()
fileprivate func setupNav() {
//Basic Setup
navigationController?.navigationBar.isTranslucent = true
navigationController?.navigationBar.setBackgroundImage(UIImage(), for: .default)
//Search Bar
searchBar.sizeToFit()
navigationItem.titleView = searchBar
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
//Implement
}
let searchController = UISearchController(searchResultsController: nil)
searchController.dimsBackgroundDuringPresentation = false
searchController.searchBar.placeholder = "Search"
searchController.searchResultsUpdater = self
navigationItem.searchController = searchController
definesPresentationContext = true
Put your SearchController in the Navigation Item instead. This will add collapsable SearchBar. Additionally you can try with Navigation Item Prompt for very small title on top.
Just for context my keyboard successfully appears so that's not the problem.
I have a searchButton as my rightBarButtonItem, when pressed it modally presents a vc that contains a SearchController. When the SearchController is presented the keyboard is also presented but the keyboard appears a second late, there's like a 1 second delay before it shows itself. Basically the vc appears on the scene and then the keyboard appears afterwards, I cannot get the keyboard to appear at the same time the SearchController is presented. I was on YouTube's and Vimeo's iOS apps and when I pressed their search button the keyboard is presented with the SearchController at the same exact time, there isn't a 1 second delay.
How can I get the keyboard to present itself at the same time the SearchController is presenting itself?
button to modally present SearchController:
#objc func searchButtonTapped() {
let searchVC = SearchController()
let nav = UINavigationController(rootViewController: searchVC)
present(nav, animated: true, completion: nil)
}
SearchController:
I've already tried adding searchController.isActive = true and searchController.searchBar.becomeFirstResponder() in DispatcQeue.main in viewWillAppear and viewDidAppear and it made no difference
class SearchController: UIViewController {
var searchController: UISearchController!
override func viewDidLoad() {
super.viewDidLoad()
searchController = UISearchController(searchResultsController: nil)
searchController.delegate = self
searchController.searchBar.delegate = self
searchController.searchResultsUpdater = self
searchController.searchBar.showsCancelButton = true
searchController.searchBar.placeholder = "Search"
searchController.searchBar.returnKeyType = .search
searchController.dimsBackgroundDuringPresentation = false
searchController.hidesNavigationBarDuringPresentation = false
searchController.searchBar.sizeToFit()
searchController.searchBar.tintColor = UIColor.black
definesPresentationContext = true
navigationItem.hidesBackButton = true
navigationItem.titleView = searchController.searchBar
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
searchController.isActive = true
}
// I tried both of these searchContrller delegate methods SEPERATELY but it made no difference, there's still a 1 second delay
func presentSearchController(_ searchController: UISearchController) {
DispatchQueue.main.async {
self.searchController.searchBar.becomeFirstResponder()
}
}
func didPresentSearchController(_ searchController: UISearchController) {
DispatchQueue.main.async {
self.searchController.searchBar.becomeFirstResponder()
}
}
}
Sure there is a delay.. ones this animation is completed, then keyboard appears.
present(nav, animated: true, completion: nil)
Please try this.
It will immediately open the keyboard if you would not preset view controller with an animation but if we present view controller animation it opens keyboard after the present animation is finished.
Thanks.
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(true)
DispatchQueue.main.async {
self.searchController.searchBar.becomeFirstResponder()
}
}
I have implemented a search controller in a viewController with a table view and also a search controller, the search bar is displayed when search button is taped, search button is a UIBarButtonItem and the searchBar is shown in the navigation controller but I want to make disappear when tap on cancel button, I have tried using serarchBarDelegate protocol but nothing happens on tap here the code
class NewsTVController: UIViewController, UITableViewDataSource, UITableViewDelegate,UISearchResultsUpdating,UISearchBarDelegate{
let searchController = UISearchController(searchResultsController: nil)
override func viewDidLoad() {
super.viewDidLoad()
searchController.searchResultsUpdater = self
definesPresentationContext = true
searchController.dimsBackgroundDuringPresentation = false
searchController.hidesNavigationBarDuringPresentation = true
self.searchController.hidesNavigationBarDuringPresentation = false
loadNews()
}
#IBAction func searchButtonTaped(sender:UIBarButtonItem){
print("tap inside")
self.navigationItem.titleView = searchController.searchBar
//self.navigationItem.rightBarButtonItem?.action = #selector(hideSearchBar(:))
// searchController.searchBar.touchesCancelled(UITouch, with: .touchUpInside){
//}
//self.navigationItem.rightBarButtonItem.
}
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
hideSearchBar()
}
func hideSearchBar() {
print("hay que escpder")
//navigationItem.setLeftBarButtonItem(searchBarButtonItem, animated: true)
//logoImageView.alpha = 0
UIView.animate(withDuration: 0.3, animations: {
self.navigationItem.titleView = nil// = self.logoImageView
self.navigationItem.title = "Eventos"
//self.logoImageView.alpha = 1
}, completion: { finished in
})
}
}
here the pic of the navigation controller with a search button and the search bar
what I want is that when the cancel button is tapped the navigation bar stay as in first pic, no search bar.
and some tips there is a way to hide the back button and the search button when search bar appears.
as i don´t find a way to achive this i found this solution
#IBAction func searchButtonTaped(sender:UIBarButtonItem){
print("tap inside")
self.navigationItem.titleView = searchController.searchBar
searchController.searchBar.setShowsCancelButton(false, animated: false)
self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Cancelar", style: .plain, target: self, action: #selector(hideSearchBar(sender:)))
//self.searchIcon.is
//self.navigationItem.rightBarButtonItem?.action = #selector(hideSearchBar(:))
// searchController.searchBar.touchesCancelled(UITouch, with: .touchUpInside){
//}
//self.navigationItem.rightBarButtonItem.
}
func hideSearchBar(sender:UIBarButtonItem) {
print("hay que escpder")
self.navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.search, target: self, action: #selector(searchButtonTaped(sender:)))
//logoImageView.alpha = 0
UIView.animate(withDuration: 0.3, animations: {
self.navigationItem.titleView = nil// = self.logoImageView
self.navigationItem.title = "Noticias"
//self.logoImageView.alpha = 1
}, completion: { finished in
})
}
and boila the search bar now dissapear from navigation controller nad the search button is functional since is linked to searchButtonTaped in Interface Builder